在jQuery中触发CSS类更改事件


240

如果使用jQuery添加或更改CSS类,如何触发事件?更改CSS类是否会触发jQuery change()事件?


8
7年后,随着现代浏览器中MutationObservers的广泛采用,此处接受的答案应该真的更新为Br先生的
joelmdev


完成杰森的回答后,我发现了这一点:stackoverflow.com/a/19401707/1579667
Benj

Answers:


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

43
触发是一种机制,该机制将允许某些元素“订阅”事件。这样,当事件被触发时,这些事件可以一次全部发生并运行其各自的功能。例如,如果我有一个对象从红色更改为蓝色,并且有三个对象等待其更改,则当它更改时,我可以触发该changeColor事件,并且订阅该事件的所有那些对象都可以做出相应的反应。
杰森

1
OP想要监听'cssClassChanged'事件似乎有点奇怪。当然,该类是由于其他“应用程序”事件(例如“ colourChanged”)的结果而添加的,通过触发来进行通告更有意义,因为我无法完全想象为什么会有人对className更改特别感兴趣,肯定是cla​​ssNameChange的结果?
riscarrott

如果特别是感兴趣的className更改,那么我想装饰jQuery.fn.addClass来触发'cssClassChanged'会更明智,就像@micred说的那样
riscarrott

5
@riscarrott我不假定知道OP的应用程序。我只是向他们介绍pub / sub的概念,他们可以根据需要应用它。用给出的信息量来争论实际的事件名称应该是愚蠢的。
杰森

140

恕我直言,更好的解决方案是结合@ RamboNo5和@Jason的两个答案

我的意思是重写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”,但似乎所有类更改(即,在页面上的任何元素上)都由该处理程序处理了……有什么想法吗?
Filipe Correia

它对我来说很好@FilipeCorreia。您能提供一个复制案例的jsfiddle吗?
Arash Milani 2013年

@ Goldentoa11您应该在某个晚上或周末花点时间,监视在大量使用广告的典型页面加载中发生的一切。大量尝试执行此类操作的脚本(有时以最壮观的方式未成功)将使您震惊。我遇到的页面每秒触发数十个DOMNodeInserted / DOMSubtreeModified事件$(),当您到达时,当实际操作开始时,总共会触发数百个事件。
L0j1k 2014年

您可能还想支持在removeClass上触发相同的事件
Darius

4
Arash,我想我明白@FilipeCorreia为何会遇到这种方法的问题:如果将此cssClassChanged事件绑定到元素,则必须知道即使该事件的子级更改了该类,它也会被触发。因此,例如,如果您不想为他们触发此事件,只需$("#YourExampleElementID *").on('cssClassChanged', function(e) { e.stopPropagation(); });在绑定之后添加类似内容即可。无论如何,非常好的解决方案,谢谢!
tonix

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。请重新考虑编辑,这是正确的方法。
Br Br 2014年

1
insertionQ库让你抓住DOM节点显示出来,如果他们选择匹配。它不使用,因此具有更广泛的浏览器支持DOMMutationObserver
Dan Dascalescu

这是一个很好的解决方案。特别是用于在添加或删除类时启动和停止画布或SVG动画。就我而言,确保它们在滚动到视线之外时未运行。甜!
BBaysinger

1
对于2019年,这应该是公认的答案。现在,所有主要的浏览器似乎都支持Mutation Observers,并且该解决方案不需要手动触发事件或重写基本的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


37
似乎是混淆未来维护者的好方法。
roufamatic 2011年

1
不要忘记返回原始方法的结果。
Petah'2

1
作为参考,您使用的是代理模式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));

用法非常简单,只需在要观察的节点上添加一个新的listender即可,并照常操作这些类:

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 2014年

1
@slashdottir您能创建一个小提琴并解释一下到底什么不起作用
yckart 2014年

是的...不起作用。TypeError:this [0]在«return this [0] .className;»中未定义
Pedro Ferreira

它有效,但有时(为什么?)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
            });

//其中具有更新的div类中的任何代码所需条目这是在像$ $ target.addClass目标(“搜索级”);


好的解决方案,我正在使用它。但是,我正在使用react,问题之一是每次虚拟生成Web元素时都要添加一个观察者。有没有办法检查观察者是否已经在场?
米凯尔·梅耶

我认为这已经
过时了,

不建议使用@ WebDude0482 MutationObserver.observe,因为它是“ MutationObserver”的“实例方法”。参见developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
珠穆朗玛峰

8

change()添加或删除CSS类或定义更改时不会触发。在选择或未选择选择框值的情况下触发。

我不确定您是说要更改CSS类定义(可以通过编程完成,但乏味且通常不建议这样做),或者是否将类添加或删除到元素中。无论哪种情况,都无法可靠地捕获到这种情况。

您当然可以为此创建自己的事件,但这只能描述为咨询性事件。它不会捕获不是您执行的代码。

或者,您可以addClass()在jQuery中重写/替换(etc)方法,但是通过香草Javascript完成时,这不会捕获(尽管我猜您也可以替换那些方法)。


8

还有一种触发自定义事件的方法

监视Html元素CSS更改的jQuery插件,作者Rick Strahl

从上面报价

Watch插件的工作方式是,将FireFox中的DOMAttrModified挂钩,将Internet Explorer中的onPropertyChanged挂钩,或者将计时器与setInterval配合使用以处理其他浏览器的更改检测。不幸的是,WebKit目前不支持DOMAttrModified,因此Safari和Chrome当前必须使用较慢的setInterval机制。


6

好问题。我正在使用Bootstrap下拉菜单,并且需要在隐藏Bootstrap下拉菜单时执行一个事件。打开下拉列表时,包含类名称为“ button-group”的div添加一个类“ open”;并且按钮本身的“ aria-expanded”属性设置为true。当下拉列表关闭时,该类“打开”将从包含的div中删除,并且aria-expanded从true切换为false。

这使我想到了一个问题,即如何检测班级变化。

使用Bootstrap时,可以检测到“下降事件”。在此链接中查找“下降事件”。 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');
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.