Ist es möglich, ein "Style Change" -Ereignis anzuhören?

206

Ist es möglich, in jQuery einen Ereignis-Listener zu erstellen, der an Stiländerungen gebunden werden kann? Wenn ich beispielsweise etwas "tun" möchte, wenn ein Element die Abmessungen ändert, oder andere Änderungen am Stilattribut, könnte ich Folgendes tun:

$('div').bind('style', function() {
    console.log($(this).css('height'));
});

$('div').height(100); // yields '100'

Es wäre wirklich nützlich.

Irgendwelche Ideen?

AKTUALISIEREN

Es tut mir leid, dass ich das selbst beantwortet habe, aber ich habe eine nette Lösung geschrieben, die zu jemand anderem passen könnte:

(function() {
    var ev = new $.Event('style'),
        orig = $.fn.css;
    $.fn.css = function() {
        $(this).trigger(ev);
        return orig.apply(this, arguments);
    }
})();

Dadurch wird die interne prototype.css-Methode vorübergehend überschrieben und am Ende mit einem Trigger neu definiert. So funktioniert es:

$('p').bind('style', function(e) {
    console.log( $(this).attr('style') );
});

$('p').width(100);
$('p').css('color','red');
David Hellsing
quelle
Ja, nette Lösung, aber in einer echten Anwendung vermute ich, dass dies eine Menge Ressourcen in Anspruch nimmt und sogar den Browser langsam erscheinen lässt, um mit den Interaktionen des Benutzers Schritt zu halten. Ich würde gerne wissen, ob das der Fall ist oder nicht.
Pixeline
1
@pixeline: Ich kann nicht sehen, dass es so viel langsamer sein sollte. jQuery ruft den CSS-Prototyp trotzdem auf, ich füge ihm nur einen Trigger hinzu. Es ist also im Grunde nur ein zusätzlicher Methodenaufruf.
David Hellsing
Ich verstehe das: Es scheint nur, dass css () im Fluss eines Benutzers, der mit einer Anwendung interagiert, viel Zeit benötigt. Abhängig davon, was Sie mit diesem Trigger tun (wenn es sich nur um ein Konsolenprotokoll handelt, sind Sie natürlich sicher), kann dies zu Problemen führen. Ich wundere mich nur. Das hängt auch von der Anwendung ab. Ich persönlich neige dazu, viel visuelles Feedback in meinen Sachen zu machen.
Pixeline
Es gibt noch ein paar Dinge zu tun, damit es mit vorhandenen JQuery-CSS-Aufrufen funktioniert: 1) Sie möchten das Element aus Ihrer neuen CSS-Funktion zurückgeben, da es sonst fließende CSS-Aufrufe unterbricht. z.B. $ ('p'). css ('display', 'block'). css ('color', 'black'); usw. 2) Wenn es sich um einen Aufruf eines Eigenschaftswerts handelt (z. B. 'obj.css (' height ');'), möchten Sie den Rückgabewert der ursprünglichen Funktion zurückgeben. Dazu überprüfe ich, ob die Argumente aufgelistet sind denn die Funktion enthält nur ein Argument.
Theringostarrs
1
Ich habe eine robustere Version dieses Ansatzes erstellt, die auch funktioniert, wenn das Stilattribut selbst entfernt wird. Es wird ein 'Stil'-Ereignis für jeden Stil ausgelöst, der auf diese Weise entfernt wird. gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema

Antworten:

19

Da jQuery Open Source ist, würde ich vermuten, dass Sie die cssFunktion optimieren können, um bei jedem Aufruf eine Funktion Ihrer Wahl aufzurufen (Übergabe des jQuery-Objekts). Natürlich sollten Sie den jQuery-Code durchsuchen, um sicherzustellen, dass intern nichts anderes zum Festlegen von CSS-Eigenschaften verwendet wird. Idealerweise möchten Sie ein separates Plugin für jQuery schreiben, damit es die jQuery-Bibliothek selbst nicht beeinträchtigt. Sie müssen jedoch entscheiden, ob dies für Ihr Projekt möglich ist oder nicht.

Josh Stodola
quelle
1
Aber was würde in diesem Fall passieren, wenn ich die style-Eigenschaft direkt mit DOM-Funktionen festlegen würde?
Jaime Hablutzel
Hier ist ein vollständiges Beispiel für diese Lösung: gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema
272

Seit die Frage gestellt wurde, haben sich die Dinge etwas weiterentwickelt. Es ist jetzt möglich, einen MutationObserver zu verwenden, um Änderungen im 'style'-Attribut eines Elements zu erkennen, ohne dass jQuery erforderlich ist:

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutationRecord) {
        console.log('style changed!');
    });    
});

var target = document.getElementById('myId');
observer.observe(target, { attributes : true, attributeFilter : ['style'] });

Das Argument, das an die Rückruffunktion übergeben wird, ist ein MutationRecord- Objekt, mit dem Sie die alten und neuen Stilwerte abrufen können.

Die Unterstützung ist in modernen Browsern einschließlich IE 11+ gut.

Codebox
quelle
19
Beachten Sie, dass dies nur mit Inline-Stilen funktioniert, nicht, wenn sich der Stil infolge von Klassenänderungen oder @media-änderungen ändert.
Fregante
2
@ bfred.it hätten Sie eine Idee, wie Sie genau das erreichen können? Ich möchte einige js ausführen, nachdem die CSS-Regeln einer Klasse auf ein Element angewendet wurden.
Kragalon
Sie könnten getComputedStylemit verwenden, setIntervalaber das würde Ihre Website sehr verlangsamen.
fregante
Hinweis: MutationObserver funktioniert nicht in allen Versionen von Android 4.4, trotz der Aussagen von caniuse.
James Gould
In diesem Twiddle finden Sie ein Beispiel für das Ändern der Hintergrundfarbe eines Textbereichs in Blau, wenn die Breite des Textbereichs über 300 Pixel hinaus gedehnt wird. jsfiddle.net/RyanNerd/y2q8wuf0 (leider scheint es einen Fehler in FF zu geben, der das Skript umHTMLElement.style.prop = "value"
RyanNerd
18

Die Deklaration Ihres Ereignisobjekts muss sich in Ihrer neuen CSS-Funktion befinden. Andernfalls kann das Ereignis nur einmal ausgelöst werden.

(function() {
    orig = $.fn.css;
    $.fn.css = function() {
        var ev = new $.Event('style');
        orig.apply(this, arguments);
        $(this).trigger(ev);
    }
})();
Mike Allen
quelle
Grosses Dankeschön. Einige Leute sagen, ändern Sie die ursprüngliche Quelle, aber Ihre Erweiterung ist die beste. Ich hatte einige Probleme, aber schließlich funktioniert es.
ccsakuweb
Es wird nach Ausführung dieses Codes kontinuierlich ausgelöst und erreicht nicht einmal das erforderliche Ergebnis.
smkrn110
13

Ich denke, die beste Antwort von Mike für den Fall, dass Sie Ihre Veranstaltung nicht starten können, weil sie nicht aus Ihrem Code stammt. Aber ich bekomme einige Fehler, wenn ich es benutzt habe. Also schreibe ich eine neue Antwort, um Ihnen den Code zu zeigen, den ich verwende.

Erweiterung

// Extends functionality of ".css()"
// This could be renamed if you'd like (i.e. "$.fn.cssWithListener = func ...")
(function() {
    orig = $.fn.css;
    $.fn.css = function() {
        var result = orig.apply(this, arguments);
        $(this).trigger('stylechanged');
        return result;
    }
})();

Verwendung

// Add listener
$('element').on('stylechanged', function () {
    console.log('css changed');
});

// Perform change
$('element').css('background', 'red');

Ich habe eine Fehlermeldung erhalten, weil var ev = new $ .Event ('style'); So etwas wie Stil wurde in HtmlDiv nicht definiert. Ich habe ihn entfernt und starte jetzt $ (this) .trigger ("stylechanged"). Ein weiteres Problem war, dass Mike das Ergebnis von $ (css, ..) nicht zurückgegeben hat. In einigen Fällen kann es zu Problemen kommen. Also bekomme ich das Ergebnis und gebe es zurück. Jetzt funktioniert ^^ In jeder CSS-Änderung sind einige Bibliotheken enthalten, die ich nicht ändern und ein Ereignis auslösen kann.

ccsakuweb
quelle
Sehr hilfreich und klug
dgo
5

Wie andere vorgeschlagen haben, können Sie ein benutzerdefiniertes Ereignis auslösen, wenn Sie die Kontrolle über den Code haben, der den Stil des Elements ändert: Wenn Sie die Höhe des Elements ändern:

$('#blah').bind('height-changed',function(){...});
...
$('#blah').css({height:'100px'});
$('#blah').trigger('height-changed');

Andernfalls können Sie, obwohl ziemlich ressourcenintensiv, einen Timer einstellen, der regelmäßig nach Änderungen an der Höhe des Elements sucht ...

Droo
quelle
Dies ist in der Tat eine sehr nette Lösung, wenn man Zugriff auf den Code hat, der die Stile ändert.
Umair Hafeez
2

Es gibt keine eingebaute Unterstützung für das Stiländerungsereignis in jQuery oder in Java Script. JQuery unterstützt jedoch das Erstellen und Abhören von benutzerdefinierten Ereignissen. Bei jeder Änderung sollten Sie jedoch die Möglichkeit haben, diese selbst auszulösen. Es wird also keine vollständige Lösung sein.

Teja Kantamneni
quelle
2

Sie können das Jquery-Plugin ausprobieren. Es löst Ereignisse aus, wenn CSS geändert wird, und es ist einfach zu verwenden

http://meetselva.github.io/#gist-section-attrchangeExtension
 $([selector]).attrchange({
  trackValues: true, 
  callback: function (e) {
    //console.log( '<p>Attribute <b>' + e.attributeName +'</b> changed from <b>' + e.oldValue +'</b> to <b>' + e.newValue +'</b></p>');
    //event.attributeName - Attribute Name
    //event.oldValue - Prev Value
    //event.newValue - New Value
  }
});
user889030
quelle
1

Interessante Frage. Das Problem ist, dass height () keinen Rückruf akzeptiert, sodass Sie keinen Rückruf starten können. Verwenden Sie entweder animate () oder css (), um die Höhe festzulegen, und lösen Sie dann das benutzerdefinierte Ereignis im Rückruf aus. Hier ist ein Beispiel mit animate (), getestet und funktioniert ( Demo ) als Proof of Concept:

$('#test').bind('style', function() {
    alert($(this).css('height'));
});

$('#test').animate({height: 100},function(){
$(this).trigger('style');
}); 
Pixeline
quelle
8
Entschuldigung, aber was hat das bewiesen? Sie trigger()das Ereignis manuell. Ich würde denken, dass das OP nach einem automatisch ausgelösten Ereignis ist, wenn ein Stilattribut geändert wird.
JPot
@JPot er zeigt, dass das height-Attribut keine Ereignisse auslöst, wenn es geändert wird.
AnthonyJClink
1

Fügen Sie einfach die @ David-Lösung von oben hinzu und formalisieren Sie sie:

Beachten Sie, dass jQuery-Funktionen verkettbar sind und 'this' zurückgeben, sodass mehrere Aufrufe nacheinander aufgerufen werden können (z $container.css("overflow", "hidden").css("outline", 0);. B. ).

Der verbesserte Code sollte also sein:

(function() {
    var ev = new $.Event('style'),
        orig = $.fn.css;
    $.fn.css = function() {
        var ret = orig.apply(this, arguments);
        $(this).trigger(ev);
        return ret; // must include this
    }
})();
Nir Hemed
quelle
0

Wie wäre es mit jQuery cssHooks?

Vielleicht verstehe ich die Frage nicht, aber was Sie suchen, ist leicht zu erledigen cssHooks, ohne die css()Funktion zu ändern .

Kopie aus der Dokumentation:

(function( $ ) {

// First, check to see if cssHooks are supported
if ( !$.cssHooks ) {
  // If not, output an error message
  throw( new Error( "jQuery 1.4.3 or above is required for this plugin to work" ) );
}

// Wrap in a document ready call, because jQuery writes
// cssHooks at this time and will blow away your functions
// if they exist.
$(function () {
  $.cssHooks[ "someCSSProp" ] = {
    get: function( elem, computed, extra ) {
      // Handle getting the CSS property
    },
    set: function( elem, value ) {
      // Handle setting the CSS value
    }
  };
});

})( jQuery ); 

https://api.jquery.com/jQuery.cssHooks/

Halbbit
quelle
-1

Ich hatte das gleiche Problem, also habe ich das geschrieben. Es funktioniert ziemlich gut. Sieht gut aus, wenn Sie es mit einigen CSS-Übergängen mischen.

function toggle_visibility(id) {
   var e = document.getElementById("mjwelcome");
   if(e.style.height == '')
      e.style.height = '0px';
   else
      e.style.height = '';
}
KingLouie
quelle