Czy można usłyszeć wydarzenie „zmiany stylu”?

206

Czy w jQuery można utworzyć detektor zdarzeń, który można powiązać ze zmianami stylu? Na przykład, jeśli chcę coś „zrobić”, gdy element zmienia wymiary lub inne zmiany atrybutu stylu, mogę:

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

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

Byłoby to naprawdę przydatne.

Jakieś pomysły?

AKTUALIZACJA

Przepraszam, że sam na to odpowiedziałem, ale napisałem dobre rozwiązanie, które może pasować do kogoś innego:

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

Spowoduje to tymczasowe zastąpienie wewnętrznej metody prototype.css i przedefiniowanie jej za pomocą wyzwalacza na końcu. Działa to tak:

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

$('p').width(100);
$('p').css('color','red');
David Hellsing
źródło
tak, fajne rozwiązanie, ale w prawdziwej aplikacji podejrzewam, że może to zająć dużo zasobów, a nawet sprawić, że przeglądarka wydaje się być powolna, aby nadążyć za interakcjami użytkownika. Chciałbym wiedzieć, czy tak jest, czy nie.
pixeline
1
@pixeline: Nie widzę, żeby było o wiele wolniej. jQuery i tak nadal wywołuje prototyp css, po prostu dodałem do niego wyzwalacz. Jest to w zasadzie tylko jedno dodatkowe wywołanie metody.
David Hellsing
Rozumiem to: po prostu wydaje się, że w przepływie użytkownika wchodzącego w interakcję z aplikacją css () nazywa się dużo czasu. W zależności od tego, co robisz z tym wyzwalaczem (jeśli jest to tylko dziennik konsoli, myślę, że jesteś oczywiście bezpieczny), może to powodować problemy. Po prostu się zastanawiam. To zależy również od zastosowania. Osobiście zwykle robię wiele wizualnych opinii w moich materiałach.
pixeline
Jest jeszcze kilka rzeczy do zrobienia, aby działały z istniejącymi wywołaniami css jquery: 1) chciałbyś zwrócić element z nowej funkcji css, bo inaczej zepsuje płynne wywołania css w stylu. na przykład. $ („p”). css („display”, „block”). css („color”, „black”); itp. 2) jeśli jest to wywołanie wartości właściwości (np. „obj.css ('height'); ') chciałbyś zwrócić wartość zwracaną pierwotnej funkcji - robię to sprawdzając, czy lista argumentów dla funkcji zawiera tylko jeden argument.
theringostarrs
1
Stworzyłem bardziej niezawodną wersję tego podejścia, która działa również po usunięciu samego atrybutu stylu . Wyrzuci zdarzenie „stylowe” dla każdego stylu usuwanego w ten sposób. gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema

Odpowiedzi:

19

Ponieważ jQuery jest oprogramowaniem typu open source, zgaduję, że można dostosować cssfunkcję, aby wywoływała wybraną funkcję za każdym razem, gdy jest wywoływana (przekazując obiekt jQuery). Oczywiście, będziesz chciał przeszukać kod jQuery, aby upewnić się, że nic innego nie używa wewnętrznie do ustawiania właściwości CSS. Najlepiej byłoby napisać osobną wtyczkę do jQuery, aby nie zakłócała ​​samej biblioteki jQuery, ale musisz zdecydować, czy jest to wykonalne dla twojego projektu.

Josh Stodola
źródło
1
Ale w tym przypadku, co by się stało, gdybym ustawił właściwość stylu bezpośrednio za pomocą funkcji DOM?
Jaime Hablutzel,
Oto kompletny przykład tego rozwiązania: gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema
272

Od czasu zadania pytania sytuacja trochę się zmieniła - można teraz używać MutationObserver do wykrywania zmian w atrybucie „stylu” elementu, nie jest wymagane 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'] });

Argument przekazywany do funkcji wywołania zwrotnego to obiekt MutationRecord, który pozwala uchwycić stare i nowe wartości stylu.

Wsparcie jest dobre w nowoczesnych przeglądarkach, w tym IE 11+.

codebox
źródło
19
Pamiętaj, że działa to tylko ze stylami wbudowanymi, a nie gdy styl zmienia się w wyniku zmiany lub @mediazmiany klasy .
fregante
2
@ bfred.it czy masz pomysł, jak to osiągnąć? Chcę uruchomić js po zastosowaniu reguł css klasy do elementu.
Kragalon
Można korzystać getComputedStylez setIntervalale to spowolnić strona dużo.
fregante 18.04.16
Uwaga: MutationObserver w rzeczywistości nie działa we wszystkich wersjach Androida 4.4, pomimo tego, co mówi caniuse.
James Gould
Zobacz ten wątek, aby zobaczyć przykład zmiany koloru tła pola tekstowego na niebieski, gdy szerokość obszaru tekstowego jest rozciągnięta powyżej 300 pikseli. jsfiddle.net/RyanNerd/y2q8wuf0 (niestety wydaje się, że w FF występuje błąd, który zawiesza skrypt wHTMLElement.style.prop = "value"
RyanNerd
18

Deklaracja obiektu zdarzenia musi znajdować się w nowej funkcji css. W przeciwnym razie zdarzenie można uruchomić tylko raz.

(function() {
    orig = $.fn.css;
    $.fn.css = function() {
        var ev = new $.Event('style');
        orig.apply(this, arguments);
        $(this).trigger(ev);
    }
})();
Mike Allen
źródło
Wielkie dzięki. Niektóre osoby mówią, aby zmienić oryginalne źródło, ale twoje rozszerzenie jest najlepsze. Miałem pewne problemy, ale w końcu to działa.
ccsakuweb
Ciągle odpala po wykonaniu tego kodu, a nawet nie osiąga wymaganego rezultatu.
smkrn110
13

Myślę, że najlepszą odpowiedzią jest to, że od Mike'a nie możesz uruchomić wydarzenia, ponieważ nie pochodzi ono z twojego kodu. Ale dostaję kilka błędów, gdy go używam. Piszę więc nową odpowiedź, aby pokazać ci kod, którego używam.

Rozbudowa

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

Stosowanie

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

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

Wystąpił błąd, ponieważ var ev = new $ .Event ('style'); Coś w stylu nie zostało zdefiniowane w HtmlDiv .. Usunąłem go i uruchamiam teraz $ (this) .trigger ("stylechanged"). Innym problemem było to, że Mike nie zwrócił wyniku $ (css, ..), więc może to powodować problemy w niektórych przypadkach. Więc otrzymuję wynik i zwracam go. Teraz działa ^^ W każdej zmianie css zawierają niektóre biblioteki, których nie mogę modyfikować i wywoływać zdarzenia.

ccsakuweb
źródło
Bardzo pomocny i sprytny
dgo
5

Jak sugerują inni, jeśli masz kontrolę nad kodem zmieniającym styl elementu, możesz wywołać zdarzenie niestandardowe po zmianie wysokości elementu:

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

W przeciwnym razie, choć dość intensywnie wykorzystujący zasoby, możesz ustawić licznik, aby okresowo sprawdzał zmiany wysokości elementu ...

droo
źródło
Jest to rzeczywiście bardzo fajne rozwiązanie, jeśli ktoś ma dostęp do kodu zmieniającego style.
Umair Hafeez
2

Nie ma wbudowanej obsługi zdarzenia zmiany stylu w jQuery lub w skrypcie Java. Ale jQuery obsługuje tworzenie niestandardowych zdarzeń i słuchanie ich, ale za każdym razem, gdy zachodzi zmiana, powinieneś mieć sposób, aby ją uruchomić. Nie będzie to więc kompletne rozwiązanie.

Teja Kantamneni
źródło
2

możesz wypróbować wtyczkę Jquery, która wyzwala zdarzenia, gdy zmienia się css i jest łatwy w użyciu

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
  }
});
użytkownik889030
źródło
1

Interesujące pytanie. Problem polega na tym, że wysokość () nie przyjmuje oddzwaniania, więc nie można było uruchomić wywołania zwrotnego. Użyj funkcji animate () lub css (), aby ustawić wysokość, a następnie wyzwolić niestandardowe zdarzenie w wywołaniu zwrotnym. Oto przykład użycia animate (), przetestowane i działa ( demo ), jako dowód koncepcji:

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

$('#test').animate({height: 100},function(){
$(this).trigger('style');
}); 
pixeline
źródło
8
Przepraszam, ale co to udowodniło? Ty trigger()wydarzenie ręcznie. Wydaje mi się, że OP jest po jakimś zdarzeniu uruchamianym automatycznie, gdy modyfikowany jest atrybut stylu.
JPot
@Jot pokazuje, że atrybut wysokości nie generuje zdarzeń po zmianie.
AnthonyJClink
1

Po prostu dodajemy i formalizujemy rozwiązanie @David z góry:

Zauważ, że funkcje jQuery są łańcuchowe i zwracają „to”, dzięki czemu można wywoływać wiele wywołań jeden po drugim (np $container.css("overflow", "hidden").css("outline", 0);.).

Tak więc ulepszony kod powinien być:

(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
źródło
0

Co powiesz na jQuery cssHooks?

Może nie rozumiem pytania, ale to, czego szukasz, można łatwo zrobić cssHooksbez zmianycss() funkcji.

kopia z dokumentacji:

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

półbitka
źródło
-1

Miałem ten sam problem, więc to napisałem. Działa raczej dobrze. Wygląda świetnie, jeśli połączysz go z niektórymi przejściami CSS.

function toggle_visibility(id) {
   var e = document.getElementById("mjwelcome");
   if(e.style.height == '')
      e.style.height = '0px';
   else
      e.style.height = '';
}
KingLouie
źródło