È possibile ascoltare un evento di "cambio di stile"?


206

È possibile creare un listener di eventi in jQuery che può essere associato a eventuali cambiamenti di stile? Ad esempio, se voglio "fare" qualcosa quando un elemento cambia dimensioni, o qualsiasi altra modifica dell'attributo di stile, potrei fare:

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

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

Sarebbe davvero utile.

Qualche idea?

AGGIORNARE

Mi spiace di aver risposto, ma ho scritto una soluzione chiara che potrebbe adattarsi a qualcun altro:

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

Questo sostituirà temporaneamente il metodo prototype.css interno e lo ridefinirà con un trigger alla fine. Quindi funziona così:

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

$('p').width(100);
$('p').css('color','red');

sì, bella soluzione, ma in un'applicazione reale, sospetto che ciò potrebbe richiedere molte risorse e persino rendere il browser lento per tenere il passo con le interazioni dell'utente. Mi piacerebbe sapere se è così o no.
pixeline,

1
@pixeline: non riesco a vedere che dovrebbe essere molto più lento. jQuery chiama comunque comunque il prototipo css, sto solo aggiungendo un trigger ad esso. Quindi in pratica è solo una chiamata di metodo aggiuntiva.
David Hellsing,

Lo capisco: sembra proprio che nel flusso di un utente che interagisce con un'applicazione, css () sia chiamato molto tempo. A seconda di ciò che fai con quel trigger (se è solo un registro della console, suppongo che tu sia al sicuro, ovviamente), questo potrebbe creare problemi. Mi sto solo chiedendo. Dipende anche dall'applicazione. Personalmente tendo a fare molti feedback visivi nelle mie cose.
pixeline

Ci sono ancora un paio di cose da fare per farlo funzionare con le chiamate css jquery esistenti: 1) vorrai restituire l'elemento dalla tua nuova funzione css oppure interromperà le chiamate css fluenti in stile. per esempio. $ ('p'). css ('display', 'block'). css ('color', 'black'); ecc. 2) se si tratta di una chiamata per un valore di proprietà (es. 'obj.css (' height ');') si vorrebbe restituire il valore di ritorno della funzione originale - lo faccio controllando se l'elenco degli argomenti per la funzione contiene solo un argomento.
theringostarrs,

1
Ho realizzato una versione più solida di questo approccio che funziona anche quando viene rimosso l' attributo di stile stesso . Genererà un evento "stile" per ogni stile rimosso in questo modo. gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema,

Risposte:


19

Poiché jQuery è open-source, immagino che potresti modificare la cssfunzione per chiamare una funzione di tua scelta ogni volta che viene invocata (passando l'oggetto jQuery). Ovviamente, dovrai scrutare il codice jQuery per assicurarti che non ci sia nient'altro che usi internamente per impostare le proprietà CSS. Idealmente, dovresti scrivere un plugin separato per jQuery in modo che non interferisca con la libreria jQuery stessa, ma dovrai decidere se è fattibile o meno per il tuo progetto.


1
Ma in questo caso cosa succederebbe se imposti la proprietà style direttamente con le funzioni DOM ??
Jaime Hablutzel,

Ecco un esempio completo di questa soluzione: gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema,

272

Le cose sono cambiate un po 'da quando è stata posta la domanda: ora è possibile utilizzare un MutationObserver per rilevare i cambiamenti nell'attributo' style 'di un elemento, non è necessario jQuery:

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'] });

L'argomento che viene passato alla funzione di callback è un MutationRecord oggetto che consente di acquisire i valori di stile vecchi e nuovi.

Il supporto è buono nei browser moderni, incluso IE 11+.


19
Tieni presente che questo funziona solo con gli stili incorporati, non quando lo stile cambia a seguito del cambio o del @mediacambio di classe .
fregante

2
@ bfred.it vorresti avere un'idea di come raggiungere questo obiettivo? Voglio eseguire alcuni js dopo che le regole CSS di una classe sono state applicate a un elemento.
Kragalon,

Si potrebbe usare getComputedStylecon setIntervalma che sarebbe rallentare il vostro sito web un sacco.
fregante,

Nota: MutationObserver in realtà non funziona in tutte le versioni di Android 4.4, nonostante ciò che dice caniuse.
James Gould,

Vedi questo twiddle per un esempio di cambiare il colore di sfondo di un'area di testo in blu quando la larghezza dell'area di testo è allungata oltre 300px. jsfiddle.net/RyanNerd/y2q8wuf0 (sfortunatamente sembra esserci un bug in FF che blocca la sceneggiatura aHTMLElement.style.prop = "value"
RyanNerd,

18

La dichiarazione dell'oggetto evento deve essere all'interno della nuova funzione CSS. Altrimenti l'evento può essere lanciato solo una volta.

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

Ottimo grazie. Alcune persone dicono di cambiare la fonte originale ma l'estensione è la migliore. Ho avuto dei problemi ma alla fine funziona.
ccsakuweb,

Si attiva continuamente, dopo aver eseguito questo codice, e non raggiunge nemmeno il risultato richiesto.
smkrn110,

13

Penso che la risposta migliore se da Mike nel caso in cui non sia possibile avviare il tuo evento perché non proviene dal tuo codice. Ma ottengo alcuni errori quando l'ho usato. Quindi scrivo una nuova risposta per mostrarti il ​​codice che utilizzo.

Estensione

// 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;
    }
})();

uso

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

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

Ho ricevuto un errore perché var ev = new $ .Event ('style'); Qualcosa di simile allo stile non era definito in HtmlDiv .. L'ho rimosso e ora lancio $ (questo) .trigger ("stylechanged"). Un altro problema era che Mike non ha restituito il resulto di $ (css, ..), in alcuni casi può causare problemi. Quindi ottengo il risultato e lo restituisco. Ora funziona ^^ In ogni cambio CSS includi da alcune librerie che non posso modificare e attivare un evento.


Molto utile e intelligente
dal

5

Come altri hanno suggerito, se hai il controllo su qualunque codice stia cambiando lo stile dell'elemento, potresti generare un evento personalizzato quando modifichi l'altezza dell'elemento:

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

Altrimenti, sebbene sia piuttosto dispendioso in termini di risorse, è possibile impostare un timer per verificare periodicamente la presenza di modifiche all'altezza dell'elemento ...


Questa è davvero una soluzione molto accurata se si ha accesso al codice che sta cambiando gli stili.
Umair Hafeez,

2

Non esiste un supporto integrato per l'evento di cambio stile in jQuery o nello script java. Ma jQuery supporta la creazione di eventi personalizzati e l'ascolto, ma ogni volta che c'è un cambiamento, dovresti avere un modo per attivarlo su te stesso. Quindi non sarà una soluzione completa.


2

puoi provare il plugin Jquery, innesca eventi quando css è cambiato ed è facile da usare

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
  }
});

1

Domanda interessante. Il problema è che height () non accetta un callback, quindi non saresti in grado di attivare un callback. Utilizzare animate () o css () per impostare l'altezza e quindi attivare l'evento personalizzato nel callback. Ecco un esempio usando animate (), testato e funzionante ( demo ), come prova del concetto:

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

$('#test').animate({height: 100},function(){
$(this).trigger('style');
}); 

8
Scusa, ma cosa ha dimostrato questo? È trigger()l'evento manualmente. Penso che l'OP sia dopo un evento innescato automaticamente quando viene modificato un attributo di stile.
JPot,

@JPot sta dimostrando che l'attributo height non genera eventi quando viene modificato.
AnthonyJClink,

1

Basta aggiungere e formalizzare la soluzione di @David dall'alto:

Si noti che le funzioni jQuery sono concatenabili e restituiscono 'this' in modo che più invocazioni possano essere chiamate una dopo l'altra (ad es. $container.css("overflow", "hidden").css("outline", 0); .).

Quindi il codice migliorato dovrebbe essere:

(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
    }
})();

0

Che ne dici di jQuery cssHooks?

Forse non capisco la domanda, ma ciò che stai cercando è facilmente realizzabile cssHooks, senza cambiarecss() funzione.

copia dalla documentazione:

(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/


-1

Ho avuto lo stesso problema, quindi ho scritto questo. Funziona piuttosto bene. Sembra fantastico se lo mescoli con alcune transizioni CSS.

function toggle_visibility(id) {
   var e = document.getElementById("mjwelcome");
   if(e.style.height == '')
      e.style.height = '0px';
   else
      e.style.height = '';
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.