在 jQuery 中触发 CSS 类更改的事件

Posted

技术标签:

【中文标题】在 jQuery 中触发 CSS 类更改的事件【英文标题】:Firing events on CSS class changes in jQuery 【发布时间】:2010-12-29 08:00:02 【问题描述】:

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

【问题讨论】:

7 年后,随着 MutationObservers 在现代浏览器中的广泛采用,这里接受的答案应该真正更新为Mr Br's。 这可能会对您有所帮助。 ***.com/questions/2157963/… 在完成杰森的回答后,我发现了这个:***.com/a/19401707/1579667 【参考方案1】:

每当您更改脚本中的类时,您都可以使用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>

More info on jQuery Triggers

【讨论】:

触发事件然后调用函数是怎么回事?为什么不直接调用函数,而不是触发呢? 触发是一种允许某些元素“订阅”事件的机制。这样,当事件被触发时,这些事件可以同时发生并运行它们各自的功能。例如,如果我有一个从红色变为蓝色的对象和三个等待它更改的对象,当它发生更改时,我可以触发changeColor 事件,所有订阅该事件的对象都可以做出相应的反应。 OP 想要监听 'cssClassChanged' 事件似乎有点奇怪;当然,该类是由于其他一些“应用程序”事件(例如“ colourChanged”)而添加的,这将更有意义,因为我无法完全想象为什么会有任何东西对特定的类名更改感兴趣,这更多的是肯定是 classNameChange 的结果? 如果它是一个特别感兴趣的类名更改,那么我想像@micred 所说的那样装饰 jQuery.fn.addClass 来触发“cssClassChanged”会更明智 @riscarrott 我不认为知道 OP 的应用程序。我只是向他们介绍了发布/订阅的概念,他们可以随意应用它。用给出的信息量来争论实际的事件名称应该是什么是愚蠢的。【参考方案2】:

恕我直言,更好的解决方案是将@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”,但似乎所有类更改(即,在我页面上的任何元素上)都由这个处理程序......有什么想法吗? 它适用于我@FilipeCorreia。您能否提供一个 jsfiddle 来复制您的案例? @Goldentoa11 您应该在某个晚上或周末抽出时间来监控在大量使用广告的典型页面加载上发生的所有事情。你会被大量尝试(有时以最壮观的方式不成功)做这样的事情的脚本所震撼。我遇到过每秒触发数十个 DOMNodeInserted/DOMSubtreeModified 事件的页面,当您到达$() 时,总共有数百个事件开始。 您可能还希望支持在 removeClass 上触发相同的事件 Arash,我想我理解为什么@FilipeCorreia 在这种方法中遇到了一些问题:如果你将这个cssClassChanged 事件绑定到一个元素,你必须知道即使它的子元素也会被触发改变他们的班级。因此,例如,如果您不想为他们触发此事件,只需在绑定后添加 $("#YourExampleElementID *").on('cssClassChanged', function(e) e.stopPropagation(); ); 之类的内容。无论如何,非常好的解决方案,谢谢!【参考方案3】:

如果你想检测类变化,最好的方法是使用 Mutation Observers,它可以让你完全控制任何属性变化。但是,您需要自己定义侦听器,并将其附加到您正在侦听的元素中。好消息是,一旦添加了侦听器,您就不需要手动触发任何东西。

$(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”侦听器附加到您要观察的元素。

【讨论】:

这似乎与接受的答案完全相同,但代码更多,在某些浏览器上不受支持,并且灵活性较低。这似乎具有的唯一好处是它会触发每次页面上的某些更改,这绝对会破坏您的性能...... 您在回答中的陈述不正确@Json,如果您不想手动激活任何触发器,但让它自动激活,这实际上是这样做的方法。这不仅是好处,而且是好处,因为有人问过这个问题。其次,这只是一个示例,正如示例中所说,不建议将事件侦听器附加到每个元素,而只附加到您想要侦听的元素,所以我不明白为什么它会破坏性能。最后一点,它是未来,大多数最新的浏览器都支持它,caniuse.com/#feat=mutationobserver。请重新考虑编辑,这是正确的方式。 insertionQ library 可让您捕获 DOM 节点是否与选择器匹配。它具有更广泛的浏览器支持,因为它不使用DOMMutationObserver 这是一个很好的解决方案。特别是在添加或删除类时启动和停止画布或 SVG 动画。在我的情况下,确保它们在滚动出视图时没有运行。甜! 对于 2019 年,这应该是公认的答案。现在所有主流浏览器似乎都支持 Mutation Observers,而且这个解决方案不需要手动触发事件或重写基本的 jQuery 功能。【参考方案4】:

我建议你重写 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
);

【讨论】:

将此与 Jason 的 $(mySelector).trigger('cssClassChanged') 结合起来是我认为的最佳方法。 有一个插件可以做到这一点:github.com/aheckmann/jquery.hook/blob/master/jquery.hook.js 似乎是迷惑未来维护者的好方法。 别忘了返回原方法的结果。 作为参考,您使用的是代理模式en.wikipedia.org/wiki/Proxy_pattern【参考方案5】:

只是一个概念证明:

查看要点以查看一些注释并保持最新:

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 你能创建一个小提琴并准确解释什么不起作用 是的...不起作用。 TypeError: this[0] is undefined in «return this[0].className;» 它可以工作,但有时(为什么?),this[0] 未定义...只需将行尾替换为 var oldClassvar newClass 为以下赋值:= (this[0] ? this[0].className : "");跨度> 【参考方案6】:

使用最新的 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
            );

// 任何更新具有类 required-entry 的 div 的代码,该类在 $target 中 比如 $target.addClass('search-class');

【讨论】:

不错的解决方案,我正在使用它。但是,我正在使用 react,其中一个问题是每次虚拟生成 web 元素时我都会添加一个观察者。有没有办法检查观察者是否已经在场? 我认为这个已经贬值了,不应该按照[link]developer.mozilla.org/en-US/docs/Web/javascript/Reference/…使用 @WebDude0482 MutationObserver.observe 未被弃用,因为它是“MutationObserver”的“实例方法”。见developer.mozilla.org/en-US/docs/Web/API/MutationObserver【参考方案7】:

change() 在添加或删除 CSS 类或定义更改时不会触发。它在选择框值被选中或取消选中等情况下触发。

我不确定您的意思是更改了 CSS 类定义(可以通过编程方式完成,但很繁琐,通常不推荐),或者是否向元素添加或删除了类。无论哪种情况,都无法可靠地捕捉到这种情况。

您当然可以为此创建自己的活动,但这只能描述为咨询。它不会捕获不属于您的代码。

或者,您可以覆盖/替换 jQuery 中的 addClass() (等)方法,但这不会在通过 vanilla Javascript 完成时捕获(尽管我猜您也可以替换这些方法)。

【讨论】:

【参考方案8】:

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

A jQuery Plug-in to monitor html Element CSS Changes by Rick Strahl

从上面引用

手表插件通过连接到 FireFox 中的 DOMAttrModified 来工作, 在 Internet Explorer 中使用 onPropertyChanged,或者使用带有 setInterval 来处理检测其他浏览器的变化。 不幸的是,WebKit 始终不支持 DOMAttrModified 现在 Safari 和 Chrome 目前不得不使用速度较慢的 setInterval 机制。

【讨论】:

【参考方案9】:

好问题。我正在使用 Bootstrap 下拉菜单,并且需要在 Bootstrap 下拉菜单被隐藏时执行事件。当下拉菜单打开时,包含类名为“button-group”的div添加了一个类“open”;并且按钮本身的“aria-expanded”属性设置为true。当下拉菜单关闭时,“open”类会从包含的 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 检测打开或关闭事件的开发人员会发现这很有帮助。像我一样,他们最初可能会走上简单地尝试检测元素类更改的道路(这显然不是那么简单)。谢谢。

【讨论】:

【参考方案10】:

如果您需要触发特定事件,您可以重写方法 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
    // ...
);

【讨论】:

【参考方案11】:

您可以绑定 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/

【讨论】:

【参考方案12】:

如果您首先知道是什么事件改变了班级,您可以对同一事件稍作延迟并检查班级。 例子

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

【讨论】:

【参考方案13】:
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');
);

【讨论】:

以上是关于在 jQuery 中触发 CSS 类更改的事件的主要内容,如果未能解决你的问题,请参考以下文章

在jq中,当触发点击事件后,在此事件没完成之前,不能再触发点击事件,在事件完成后才能再次点击,该怎么弄

如何使用jQuery触发类更改事件?

JQ模拟触发事件

jquery常用函数

layui如何自动触发change事件,可以用jquery的change吗?

jq 在iframe中点击按钮,父元素触发事件