jQueryでのCSSクラス変更のイベントの発生


240

CSSクラスがjQueryを使用して追加または変更された場合、イベントを起動するにはどうすればよいですか?CSSクラスを変更すると、jQuery change()イベントが発生しますか?


8
7年後、最近のブラウザーでMutationObserversがかなり広く採用されたことでここで受け入れられた回答は、Br氏に実際に更新されるはずです。
joelmdev 2016年

これは役立つかもしれません。stackoverflow.com/questions/2157963/…–
PSR

ジェイソンの答えの完了時に、私はこれを見つけました:stackoverflow.com/a/19401707/1579667
Benj

回答:


223

スクリプトでクラスを変更するときはいつでも、a triggerを使用して独自のイベントを発生させることができます。

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

しかし、そうでなければ、いいえ、クラスが変更されたときにイベントを発生させるための組み込みの方法はありません。change()フォーカスが変更された入力を離れた後にのみ発生します。

$(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トリガーの詳細


4
次に、関数を呼び出すイベントをトリガーすることは何ですか?関数をトリガーするのではなく、直接呼び出すのはなぜですか?
RamboNo5 2009

43
トリガーは、特定の要素がイベントに「サブスクライブ」できるようにするメカニズムです。このようにして、イベントがトリガーされると、それらのイベントはすべて一度に発生し、それぞれの機能を実行できます。たとえば、赤から青に変化する1つのオブジェクトとそれが変化するのを待っている3つのオブジェクトがある場合、変化したときにchangeColorイベントをトリガーするだけで、そのイベントにサブスクライブしているすべてのオブジェクトがそれに応じて反応できます。
Jason、

1
OPが「cssClassChanged」イベントをリッスンしたいのは少し奇妙に思えます。確かにクラスは、「colourChanged」などの他の「アプリケーション」イベントの結果として追加されたもので、特にclassNameの変更に関心がある理由がよくわからないので、トリガーで通知する方が理にかなっています。確かにclassNameChangeの結果?
riscarrott 2013

特に興味深いのがclassNameの変更だった場合は、jQuery.fn.addClassを装飾して「cssClassChanged」をトリガーする方が、@ micredが言ったように賢明だと思います
riscarrott

5
@riscarrott OPのアプリケーションを知っているとは思いません。私はpub / subのコンセプトを提示しているだけで、好きなように適用できます。与えられた情報の量と実際のイベント名がどうあるべきかについての議論はばかげています。
Jason

140

私見より良い解決策は、@ RamboNo5と@Jasonによる2つの回答を組み合わせることです

つまり、addClass関数をオーバーライドして、カスタムイベントを 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
    });
});

これは確かに最良のアプローチのように聞こえますが、 "#YourExampleElementID"にのみイベントをバインドしますが、すべてのクラスの変更(つまり、ページ上の任意の要素)はこのハンドラーによって処理されているようです。
フィリペコレイア2013年

@FilipeCorreiaで問題なく動作します。ケースの複製をjsfiddleに提供できますか?
Arash Milani 2013年

@ Goldentoa11夕方または週末に時間をかけて、広告を頻繁に使用する一般的なページロードで発生するすべてを監視する必要があります。このようなことをしようとするスクリプトの量に驚かされます(最も壮観な方法で失敗することもあります)。毎秒数十のDOMNodeInserted / DOMSubtreeModifiedイベントを発生させ$()、実際のアクションが始まるに到達するまでに何百ものイベントが発生するページを横断しました。
L0j1k 2014

おそらく、removeClassで同じイベントをトリガーすることもサポートしたいと思うでしょう
Darius

4
アラシュ、@ FilipeCorreiaがこのアプローチでいくつかの問題を抱えていた理由は理解できたと思います。このcssClassChangedイベントを要素にバインドすると、その子がクラスを変更した場合でもイベントが発生することに注意する必要があります。したがって、たとえばこのイベントを発生させたくない場合は$("#YourExampleElementID *").on('cssClassChanged', function(e) { e.stopPropagation(); });、バインドの後に何かを追加します。とにかく、本当に素晴らしい解決策、ありがとう!
tonix 2017年

59

クラスの変更を検出したい場合、最善の方法は変異オブザーバーを使用することです。これにより、属性の変更を完全に制御できます。ただし、リスナーを自分で定義し、リスニングしている要素に追加する必要があります。良い点は、リスナーが追加されたら手動で何かをトリガーする必要がないことです。

$(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
    }

});
});

この例では、イベントリスナーがすべての要素に追加されていますが、ほとんどの場合は必要ありません(メモリを節約します)。この「attrchange」リスナーを監視する要素に追加します。


1
これは、受け入れられた回答が行っているのとまったく同じことを行っているようですが、コードが多く、一部のブラウザーではサポートされておらず、柔軟性が低くなっています。これが持っていると思われる唯一の利点は、ページで何かが変更されるたびに起動し、パフォーマンスを完全に破壊することです...
Jason

10
答えのステートメントが間違っています@Json、これは実際にはトリガーを手動でアクティブにしたくないが、自動的にアクティブにしたい場合に行う方法です。それは利益であるだけでなく、問題でもありました。次に、これは単なる例であり、例で述べているように、すべての要素にイベントリスナーを追加するのではなく、リッスンしたい要素にのみ追加することをお勧めします。そのため、パフォーマンスを損なう理由がわかりません。そして最後に、それは未来であり、最新のブラウザのほとんどがそれをサポートしています、caniuse.com / # feat = mutationobserver。編集を再考してください、それは正しい方法です。
Mr Br

1
insertionQライブラリは、彼らがセレクタに一致する場合は、あなたが現れてDOMノードをキャッチすることができます。を使用しないため、幅広いブラウザがサポートされますDOMMutationObserver
Dan Dascalescu、2015

これは素晴らしいソリューションです。特に、クラスが追加または削除されたときにキャンバスまたはSVGアニメーションを開始および停止するため。私の場合、スクロールして見えなくなったときに実行されていないことを確認します。甘い!
BBaysinger 2018

1
2019の場合、これが受け入れられる答えになるはずです。現在、すべての主要なブラウザーが変異オブザーバーをサポートしているようで、このソリューションでは、イベントを手動で起動したり、基本的なjQuery機能を書き換えたりする必要はありません。
sp00n

53

addClass関数をオーバーライドすることをお勧めします。あなたはこの方法でそれを行うことができます:

// 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
これをJasonの$(mySelector).trigger( 'cssClassChanged')と組み合わせることは、私が考える最良のアプローチでしょう。
kosoant 2009

4
これを一般的に行うプラグインがあります:github.com/aheckmann/jquery.hook/blob/master/jquery.hook.js
Jerph

37
将来のメンテナを混乱させる素晴らしい方法のようです。
roufamatic

1
元のメソッドの結果を返すことを忘れないでください。
Petah

1
参考までに、Proxy Pattern en.wikipedia.org/wiki/Proxy_pattern
Darius

22

単なる概念実証:

要点を見て注釈を確認し、最新の状態を維持してください。

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

使い方は非常に簡単です。観察したいノードに新しいリスナーを追加し、通常どおりクラスを操作します。

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メソッドでは古いクラスまたは新しいクラスを読み取ることができませんでした。
slashdottir

1
@slashdottirフィドルを作成して、うまくいかないを説明できます
yckart 2014

ええ...動作しません。TypeError:this [0]は«return this [0] .className;»で定義されていません
ペドロフェレイラ

それは動作しますが、時には(なぜ?)、this[0]定義されていません...単に持つ行の末尾置き換えるvar oldClassvar newClass、以下の割り当てとを:= (this[0] ? this[0].className : "");
fvlinden

9

最新のjqueryミューテーションを使用

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( 'search-class');のような$ targetにあるrequired-entryクラスを持つdivを更新するコード


素晴らしい解決策、私はそれを使用しています。しかし、reactを使用していますが、問題の1つは、web要素が仮想的に生成されるたびにオブザーバーを追加することです。オブザーバーがすでに存在しているかどうかを確認する方法はありますか?
ミカエル・メイヤー

これは
廃止

@ WebDude0482 MutationObserver.observeは「MutationObserver」の「インスタンスメソッド」であるため、廃止されません。developer.mozilla.org/en-US/docs/Web/API/MutationObserver
nu everest

8

change()CSSクラスが追加または削除されたとき、または定義が変更されたときに発生しません。選択ボックスの値が選択または選択解除された場合などに発生します。

CSSクラス定義が変更されているか(プログラムで行うことはできますが、面倒で一般に推奨されていません)、またはクラスが要素に追加または削除されているかどうかはわかりません。どちらの場合でも、これを確実にキャプチャする方法はありません。

もちろん、このために独自のイベントを作成することもできますが、これは助言としてのみ説明できますます。それはあなたがそれをしていないコードをキャプチャしません。

あるいはaddClass()、jQueryの(etc)メソッドをオーバーライド/置換することもできますが、これはバニラJavascriptを介して行われたときにキャプチャされません(ただし、これらのメソッドも置換できると思います)。


8

カスタムイベントトリガーしない方法がもう1つあります

Rick StrahlによるHTML要素のCSS変更を監視するためのjQueryプラグイン

上からの引用

ウォッチプラグインは、FireFoxのDOMAttrModifiedに接続するか、Internet ExplorerのonPropertyChangedに接続するか、タイマーを使用してsetIntervalで他のブラウザーの変更の検出を処理します。残念ながら、現時点ではWebKitはDOMAttrModifiedを一貫してサポートしていないため、SafariとChromeでは現在、より遅いsetIntervalメカニズムを使用する必要があります。


6

良い質問。ブートストラップドロップダウンメニューを使用していて、ブートストラップドロップダウンが非表示のときにイベントを実行する必要がありました。ドロップダウンを開くと、「button-group」というクラス名を持つdivが「open」というクラスを追加します。ボタン自体には、「aria-expanded」属性がtrueに設定されています。ドロップダウンが閉じられると、「open」のクラスが含まれているdivから削除され、aria-expandedがtrueからfalseに切り替えられます。

それで、クラスの変更をどのように検出するかというこの質問に私を導きました。

ブートストラップには、検出可能な「ドロップダウンイベント」があります。このリンクで「ドロップダウンイベント」を探します。 http://www.w3schools.com/bootstrap/bootstrap_ref_js_dropdown.asp

これは、Bootstrap Dropdownでこのイベントを使用する簡単な例です。

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

これは、質問されている一般的な質問よりも具体的であることがわかりました。しかし、Bootstrap Dropdownを使用してオープンイベントまたはクローズドイベントを検出しようとする他の開発者がこれが役立つと思います。私と同じように、彼らは最初は要素クラスの変更を検出しようとするだけの経路をたどる可能性があります(明らかにそれほど単純ではありません)。ありがとう。


5

特定のイベントをトリガーする必要がある場合は、メソッドaddClass()をオーバーライドして、「classadded」というカスタムイベントを発生させることができます。

ここに方法:

(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イベントをバインドできます。ここに例を追加します:

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

最初にどのイベントがクラスを変更したかがわかっている場合は、同じイベントでわずかな遅延を使用し、クラスをチェックすることができます。例

//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');
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.