JQuery'deki CSS sınıfı değişiklikleriyle ilgili olayları başlatma


240

JQuery kullanılarak bir CSS sınıfı eklenir veya değiştirilirse bir olayı nasıl tetikleyebilirim? CSS sınıfının değiştirilmesi jQuery change()olayını tetikliyor mu?


8
7 yıl sonra, MutationObservers'ın modern tarayıcılarda oldukça geniş bir şekilde benimsenmesiyle, buradaki kabul edilen cevap gerçekten Br Br olacak şekilde güncellenmelidir .
joelmdev

Bu size yardımcı olabilir. stackoverflow.com/questions/2157963/…
PSR

Jason'ın cevabını tamamladıktan sonra şunu buldum: stackoverflow.com/a/19401707/1579667
Benj

Yanıtlar:


223

Senaryonuzdaki bir sınıfı değiştirdiğinizde trigger, kendi etkinliğinizi yükseltmek için a kullanabilirsiniz .

$(this).addClass('someClass');
$(mySelector).trigger('cssClassChanged')
....
$(otherSelector).bind('cssClassChanged', data, function(){ do stuff });

ama aksi halde, hayır, bir sınıf değiştiğinde bir olayı ateşlemenin hiçbir yolu yoktur. change()yalnızca odak, girdisi değiştirilen bir girdiden ayrıldıktan sonra tetiklenir.

$(function() {
  var button = $('.clickme')
      , box = $('.box')
  ;
  
  button.on('click', function() { 
    box.removeClass('box');
    $(document).trigger('buttonClick');
  });
            
  $(document).on('buttonClick', function() {
    box.text('Clicked!');
  });
});
.box { background-color: red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="box">Hi</div>
<button class="clickme">Click me</button>

JQuery Triggers hakkında daha fazla bilgi


4
O zaman bir işlevi çağıran olayı tetiklemenin nesi var? Neden fonksiyonu tetiklemek yerine doğrudan çağırmıyorsunuz?
RamboNo5

43
Tetikleme, belirli öğelerin bir etkinliğe "abone olmasına" olanak tanıyan bir mekanizmadır. bu şekilde, olay tetiklendiğinde, bu olayların tümü aynı anda gerçekleşebilir ve ilgili işlevlerini çalıştırabilir. Örneğin, kırmızıdan maviye değişen bir nesne ve değişmesini bekleyen üç nesne varsa, değiştiğinde, yalnızca changeColorolayı tetikleyebilirim ve bu olaya abone olan tüm nesneler buna göre tepki verebilir.
Jason

1
OP'nin bir 'cssClassChanged' olayını dinlemek istediğini biraz garip görünüyor; şüphesiz sınıf, tetikleyici ile duyurmak için daha mantıklı olan 'colourChanged' gibi başka bir 'uygulama' olayının bir sonucu olarak eklendi, çünkü bir şeyin neden bir className değişikliğiyle ilgileneceğini tam olarak hayal edemiyorum. classNameChange sonucu mutlaka?
riscarrott

Özellikle ilgi çekici bir className değişikliği olsaydı o zaman 'cssClassChanged' tetiklemek için jQuery.fn.addClass dekorasyon hayal @micred gibi daha mantıklı olacağını düşünürdüm
riscarrott

5
@ riscarrott OP'nin uygulamasını bildiğimizi sanmıyorum. Onlara sadece pub / sub kavramını sunuyorum ve istedikleri gibi uygulayabilirler. Gerçek olay adının ne olması gerektiğini verilen bilgi miktarı ile tartışmak saçmadır.
Jason

140

IMHO daha iyi bir çözüm @ RamboNo5 ve @Jason'un iki cevabını birleştirmektir

AddClass işlevini geçersiz kılmak ve adlı özel bir olay eklemek demek cssClassChanged

// Create a closure
(function(){
    // Your base, I'm in it!
    var originalAddClassMethod = jQuery.fn.addClass;

    jQuery.fn.addClass = function(){
        // Execute the original method.
        var result = originalAddClassMethod.apply( this, arguments );

        // trigger a custom event
        jQuery(this).trigger('cssClassChanged');

        // return the original result
        return result;
    }
})();

// document ready function
$(function(){
    $("#YourExampleElementID").bind('cssClassChanged', function(){ 
        //do stuff here
    });
});

Bu gerçekten en iyi yaklaşım gibi geliyor, ancak olayı yalnızca "#YourExampleElementID" öğesine bağlasam da, tüm sınıf değişikliklerinin (yani, sayfamdaki öğelerin herhangi birinde) bu işleyici tarafından ele alındığı anlaşılıyor ... herhangi bir düşünce?
Filipe Correia

Benim için iyi çalışıyor @FilipeCorreia. Davanızı kopyalayarak bir jsfiddle sağlayabilir misiniz?
Arash Milani

@ Goldentoa11 Bir akşam veya hafta sonu zaman ayırmalı ve yoğun reklam kullanan tipik bir sayfa yükünde gerçekleşen her şeyi izlemelisiniz. Böyle şeyler yapmaya çalışan (bazen en başarısız şekilde başarısız olur) senaryo hacminden etkileneceksiniz. $()Gerçek eylem başladığında, onlarca DOMNodeInserted / DOMSubtreeModified olayı saniyede toplam yüzlerce olayı tetikleyen sayfalarda koştum .
L0j1k

Muhtemelen removeClass'ta aynı etkinliği tetiklemeyi de desteklemek istersiniz
Darius

4
Arash, sanırım @FilipeCorreia'nın bu yaklaşımla neden bazı sorunlar yaşadığını anlıyorum: bu cssClassChangedolayı bir öğeye bağlarsanız , çocuğu sınıflarını değiştirdiğinde bile tetikleneceğini bilmelisiniz. Bu nedenle, örneğin bu olayı onlar için başlatmak istemiyorsanız $("#YourExampleElementID *").on('cssClassChanged', function(e) { e.stopPropagation(); });, bağlandıktan sonra olduğu gibi bir şey ekleyin . Neyse, gerçekten güzel bir çözüm, teşekkürler!
tonix

59

Sınıf değişikliğini tespit etmek istiyorsanız, en iyi yol, herhangi bir özellik değişikliği üzerinde tam kontrol sağlayan Mutasyon Gözlemcilerini kullanmaktır. Ancak dinleyiciyi kendiniz tanımlamanız ve dinlediğiniz öğeye eklemeniz gerekir. İyi bir şey, dinleyici eklendikten sonra herhangi bir şeyi manuel olarak tetiklemenize gerek olmamasıdır.

$(function() {
(function($) {
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

    $.fn.attrchange = function(callback) {
        if (MutationObserver) {
            var options = {
                subtree: false,
                attributes: true
            };

            var observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(e) {
                    callback.call(e.target, e.attributeName);
                });
            });

            return this.each(function() {
                observer.observe(this, options);
            });

        }
    }
})(jQuery);

//Now you need to append event listener
$('body *').attrchange(function(attrName) {

    if(attrName=='class'){
            alert('class changed');
    }else if(attrName=='id'){
            alert('id changed');
    }else{
        //OTHER ATTR CHANGED
    }

});
});

Bu örnekte olay dinleyicisi her öğeye eklenir, ancak çoğu durumda bunu istemezsiniz (bellek tasarrufu). Gözlemlemek istediğiniz öğeye bu "yıpranma" dinleyicisini ekleyin.


1
Bu, kabul edilen cevabın yaptığı ile aynı şeyi yapıyor gibi görünüyor, ancak daha fazla kodla, bazı tarayıcılarda desteklenmiyor ve daha az esneklikle. bunun sahip olduğu tek fayda , sayfadaki her değişiklikte performansınızı kesinlikle yok edecek şekilde
Jason

10
Yanıttaki ifadeniz @Json yanlış, aslında herhangi bir tetikleyiciyi manuel olarak etkinleştirmek istemiyorsanız, ancak otomatik olarak etkin hale getirmek istiyorsanız bunu yapmanın yoludur. Bu sadece fayda değil, aynı zamanda söz konusu olduğu için de istendi. İkincisi, bu sadece bir örnektir ve örnekte dediği gibi olay dinleyicisini her öğeye değil, yalnızca dinlemek istediğiniz öğelere eklemeniz önerilir, bu yüzden performansı neden yok edeceğini anlamıyorum. Ve son şey, gelecek, en son tarayıcının çoğu bunu destekliyor, caniuse.com/#feat=mutationobserver.Lütfen düzenlemeyi yeniden düşünün, doğru yol.
Bay Br

1
İnsertionQ kütüphane onlar bir seçici maç eğer DOM gösterilmesini düğümleri yakalamak sağlar. Kullanılmadığı için daha geniş tarayıcı desteğine sahiptir DOMMutationObserver.
Dan Dascalescu

Bu harika bir çözüm. Özellikle bir sınıf eklendiğinde veya kaldırıldığında tuval veya SVG animasyonlarını başlatmak ve durdurmak için. Benim durumumda, görünümün dışına kaydırıldığında çalışmadığından emin olmak için. Tatlı!
BBaysinger

1
2019 için bu kabul edilen cevap olmalı. Tüm büyük tarayıcılar şimdi Mutasyon Gözlemcilerini destekliyor gibi görünüyor ve bu çözüm olayları manuel olarak başlatmayı veya temel jQuery işlevselliğini yeniden yazmayı gerektirmiyor.
sp00n

53

AddClass işlevini geçersiz kılmanızı öneririm. Bunu şu şekilde yapabilirsiniz:

// Create a closure
(function(){
    // Your base, I'm in it!
    var originalAddClassMethod = jQuery.fn.addClass;

    jQuery.fn.addClass = function(){
        // Execute the original method.
        var result = originalAddClassMethod.apply( this, arguments );

        // call your function
        // this gets called everytime you use the addClass method
        myfunction();

        // return the original result
        return result;
    }
})();

// document ready function
$(function(){
    // do stuff
});

16
Bunu Jason'ın $ (mySelector) .trigger ('cssClassChanged') ile birleştirmek bence en iyi yaklaşım olacaktır.
kosoant

4
Bunu genel olarak yapan bir eklenti var: github.com/aheckmann/jquery.hook/blob/master/jquery.hook.js
Jerph

37
Gelecekteki bir bakıcıyı karıştırmanın harika bir yolu gibi görünüyor.
roufamatic

1
Orijinal yöntemin sonucunu döndürmeyi unutmayın.
Petah

1
Referans olarak, Proxy Deseni kullanıyorsunuz tr.wikipedia.org/wiki/Proxy_pattern
Darius

22

Sadece bir kavram kanıtı:

Bazı ek açıklamaları görmek ve güncel kalmak için özete bakın:

https://gist.github.com/yckart/c893d7db0f49b1ea4dfb

(function ($) {
  var methods = ['addClass', 'toggleClass', 'removeClass'];

  $.each(methods, function (index, method) {
    var originalMethod = $.fn[method];

    $.fn[method] = function () {
      var oldClass = this[0].className;
      var result = originalMethod.apply(this, arguments);
      var newClass = this[0].className;

      this.trigger(method, [oldClass, newClass]);

      return result;
    };
  });
}(window.jQuery || window.Zepto));

Kullanımı oldukça basit, sadece gözlemlemek ve düğümleri genellikle istediğiniz gibi işlemek istediğiniz düğüme yeni bir listender ekleyin:

var $node = $('div')

// listen to class-manipulation
.on('addClass toggleClass removeClass', function (e, oldClass, newClass) {
  console.log('Changed from %s to %s due %s', oldClass, newClass, e.type);
})

// make some changes
.addClass('foo')
.removeClass('foo')
.toggleClass('foo');

RemoveClass yöntemiyle eski veya yeni sınıfı okuyamadım dışında bu benim için çalıştı.
slashdottir

1
@slashdottir Bir keman yaratabilir ve tam olarak neyin işe yaramadığını açıklayabilir misiniz ?
yckart

Evet ... çalışmıyor. TypeError: bu [0], «return this [0] .className;» klasöründe tanımlanmamış
Pedro Ferreira

o (? neden), bazen çalışır, ancak this[0]tanımlanmamış ... basitçe ile çizgilerin ucunu değiştirmek var oldClassve var newClassaşağıdaki atama ile:= (this[0] ? this[0].className : "");
fvlinden

9

en son jquery mutasyonunu kullanarak

var $target = jQuery(".required-entry");
            var observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(mutation) {
                    if (mutation.attributeName === "class") {
                        var attributeValue = jQuery(mutation.target).prop(mutation.attributeName);
                        if (attributeValue.indexOf("search-class") >= 0){
                            // do what you want
                        }
                    }
                });
            });
            observer.observe($target[0],  {
                attributes: true
            });

// $ target.addClass ('arama-sınıfı') gibi $ target içinde olan sınıf zorunlu girişine sahip div'i güncelleyen herhangi bir kod ;


Güzel çözüm, kullanıyorum. Ancak, tepki kullanıyorum ve sorunlardan biri web öğesi sanal olarak her oluşturulduğunda bir gözlemci eklemek olmasıdır. Bir gözlemcinin zaten mevcut olup olmadığını kontrol etmenin bir yolu var mı?
Mikaël Mayer


@ WebDude0482 MutationObserver.observe, "MutationObserver" ın bir "Örnek Yöntemi" olduğundan kullanımdan kaldırılmamıştır. Bkz developer.mozilla.org/en-US/docs/Web/API/MutationObserver
nu everest

8

change()CSS sınıfı eklendiğinde veya kaldırıldığında veya tanım değiştiğinde tetiklenmez. Seçim kutusu değerinin seçilmesi veya seçilmemesi gibi durumlarda tetiklenir.

CSS sınıfı tanımının değişip değişmediğini (programlı olarak yapılabilir, ancak sıkıcıdır ve genellikle önerilmez) veya bir öğeye sınıf eklendiğini veya kaldırıldığını kastediyorsanız emin değilim. Her iki durumda da bu olayı güvenilir bir şekilde yakalamanın bir yolu yoktur.

Elbette bunun için kendi etkinliğinizi yaratabilirsiniz, ancak bu sadece danışmanlık olarak tanımlanabilir . Size ait olmayan kodu yakalamaz.

Alternatif olarak, jQuery'de addClass()(etc) yöntemlerini geçersiz kılabilir / değiştirebilirsiniz, ancak vanilya Javascript ile yapıldığında bu yakalanmayacaktır (her ne kadar bu yöntemleri de değiştirebileceğinizi tahmin ediyorum).


8

Bir yol daha vardır olmadan bir özel etkinliği tetikleyen

Html Element CSS Değişikliklerini Rick Strahl tarafından izlemek için bir jQuery Eklentisi

Yukarıdan alıntı

Saat eklentisi, FireFox'ta DOMAttrModified'e, Internet Explorer'da onPropertyChanged'e veya diğer tarayıcılarda değişikliklerin algılanması için setInterval ile bir zamanlayıcı kullanarak çalışır. Ne yazık ki WebKit şu anda DOMAttrModified'i sürekli olarak desteklemediğinden Safari ve Chrome şu anda daha yavaş setInterval mekanizmasını kullanmak zorunda.


6

İyi soru. Bootstrap açılır menüsünü kullanıyorum ve bir Bootstrap açılır menüsü gizlendiğinde bir olayı yürütmek için gerekli. Açılır menü açıldığında, sınıf adı "düğme-grubu" olan içeren div "açık" sınıfı ekler; ve düğmenin kendisi de true değerine ayarlanmış "aria-expanded" özelliğine sahiptir. Açılır menü kapatıldığında, bu "açık" sınıfı içeren div'den kaldırılır ve aria-genişletilmiş, true değerinden false değerine geçirilir.

Bu beni, sınıf değişikliğinin nasıl tespit edileceği sorusuna götürdü.

Bootstrap ile algılanabilen "Açılır Olaylar" vardır. Bu bağlantıda "Açılır Etkinlikler" i bulun. http://www.w3schools.com/bootstrap/bootstrap_ref_js_dropdown.asp

İşte bu olayı Bootstrap Açılır Menüsünde kullanmanın hızlı ve kirli bir örneği.

$(document).on('hidden.bs.dropdown', function(event) {
    console.log('closed');
});

Şimdi bunun genel sorudan daha spesifik olduğunu anlıyorum. Ancak, Bootstrap Dropdown ile açık veya kapalı bir olayı tespit etmeye çalışan diğer geliştiricilerin bunu faydalı bulacağını düşünüyorum. Benim gibi, başlangıçta sadece bir element sınıfı değişikliğini tespit etmeye çalışabilirler (görünüşe göre bu kadar basit değil). Teşekkürler.


5

Belirli bir olayı tetiklemeniz gerekiyorsa, 'classadded' adlı özel bir etkinliği başlatmak için addClass () yöntemini geçersiz kılabilirsiniz.

İşte nasıl:

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

$('#myElement').on('classadded', function(ev, newClasses) {
    console.log(newClasses + ' added!');
    console.log(this);
    // Do stuff
    // ...
});

5

DOMSubtreeModified olayını bağlayabilirsiniz. Buraya bir örnek ekliyorum:

HTML

<div id="mutable" style="width:50px;height:50px;">sjdfhksfh<div>
<div>
  <button id="changeClass">Change Class</button>
</div>

JavaScript

$(document).ready(function() {
  $('#changeClass').click(function() {
    $('#mutable').addClass("red");
  });

  $('#mutable').bind('DOMSubtreeModified', function(e) {
      alert('class changed');
  });
});

http://jsfiddle.net/hnCxK/13/


0

İlk olarak hangi etkinliğin sınıfı değiştirdiğini biliyorsanız, aynı etkinlikte hafif bir gecikme ve sınıfı kontrol edin. misal

//this is not the code you control
$('input').on('blur', function(){
    $(this).addClass('error');
    $(this).before("<div class='someClass'>Warning Error</div>");
});

//this is your code
$('input').on('blur', function(){
    var el= $(this);
    setTimeout(function(){
        if ($(el).hasClass('error')){ 
            $(el).removeClass('error');
            $(el).prev('.someClass').hide();
        }
    },1000);
});

http://jsfiddle.net/GuDCp/3/


0
var timeout_check_change_class;

function check_change_class( selector )
{
    $(selector).each(function(index, el) {
        var data_old_class = $(el).attr('data-old-class');
        if (typeof data_old_class !== typeof undefined && data_old_class !== false) 
        {

            if( data_old_class != $(el).attr('class') )
            {
                $(el).trigger('change_class');
            }
        }

        $(el).attr('data-old-class', $(el).attr('class') );

    });

    clearTimeout( timeout_check_change_class );
    timeout_check_change_class = setTimeout(check_change_class, 10, selector);
}
check_change_class( '.breakpoint' );


$('.breakpoint').on('change_class', function(event) {
    console.log('haschange');
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.