jQuery检测特定类别的div元素是否已添加到DOM中

105

我正在使用.on()来绑定在页面加载后创建的div元素的事件。对于点击、鼠标进入等事件它运行得很好,但是我需要知道何时添加了一个新的class为MyClass的div元素。我正在寻找以下解决方案:

$('#MyContainer').on({

  wascreated: function () { DoSomething($(this)); }

}, '.MyClass');

我该怎么做?我已经成功地编写了整个应用程序,没有使用插件,我想保持这种方式。

谢谢。


这是你自己的代码生成元素吗?如果是,你可以在创建时进行检查并相应地采取行动。 - Surreal Dreams
是的,没错。但是使用 .on(),我可以在 document.ready 时绑定事件,这样当我生成 HTML 时就不必担心绑定问题了。我希望 DoSomething 能有同样的行为。 - frenchie
你可以使用 $("div/your-selector").hasClass("MyClass"); 来检查一个 div 是否有特定的类。我猜你可以将这个 API 应用到你的项目中。 - KBN
.on() 只是用于事件。你可以为自定义事件设置它,但当你创建新元素时仍然需要触发该事件。 - Surreal Dreams
@xFourtyFourx 这只能在 ready() 时存在于 DOM 中的元素上工作,而 frenchie 正在动态生成元素。 - Surreal Dreams
3
重复的 当元素被添加到页面时触发事件 问题。 - Dan Dascalescu
6个回答

118

以前,人们可以钩入jQuery的 domManip 方法来捕获所有jQuery DOM操作并查看插入了哪些元素等等。 但是,自jQuery 3.0+开始,jQuery团队关闭了此功能,因为这通常不是钩入jQuery方法的好方法,并且他们使内部 domManip 方法不再在核心jQuery代码之外可用。

Mutation Events也已被弃用,因为以前可以执行类似以下内容的操作

$(document).on('DOMNodeInserted', function(e) {
    if ( $(e.target).hasClass('MyClass') ) {
       //element with .MyClass was inserted.
    }
});

应该避免使用这种方法,现在应该使用 Mutation Observers 来代替,它的工作方式如下:

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        console.log(mutation)
        if (mutation.addedNodes && mutation.addedNodes.length > 0) {
            // element added to DOM
            var hasClass = [].some.call(mutation.addedNodes, function(el) {
                return el.classList.contains('MyClass')
            });
            if (hasClass) {
                // element has class `MyClass`
                console.log('element ".MyClass" added');
            }
        }
    });
});

var config = {
    attributes: true,
    childList: true,
    characterData: true
};

observer.observe(document.body, config);

Firefox和Webkit应该没问题,但IE的支持有些欠缺。它应该可以在IE9中工作,但在IE8中可能不太行。我知道我已经看到了解决方法,而且我还没有在IE中真正测试过,所以如果它不起作用,那么请先尝试一下是否有解决方法。这里有一个插件,看起来像是添加了IE支持这里,但我还没有尝试过,只是在Google搜索中找到的。 - adeneo
2
我最初使用了您的答案,但它在跨浏览器上无法正常工作是一个问题。然后,我很快找到了一个解决方法来避免这个问题。几周前,我代码中的节点插入检测问题又回来了。这一次,我在我的答案中发布的代码完全按照预期工作,在跨浏览器上没有任何错误。所以我把我的答案标记为已采纳;这就是SO的工作方式, OP决定采纳哪个答案,而群众投票评选答案。随着时间的推移,人们会投票并决定哪个答案最适合他们。 - frenchie
2
而且,我想说你曾经多次帮助我提供高质量的答案,救了我的一天。更改接受的答案为我的答案纯粹基于代码及其如何解决我需要解决的问题。 - frenchie
1
使用此代码出现了“Uncaught TypeError: Cannot read property 'contains' of undefined”错误。 - gordie
查找agrul上面的答案来解决Uncaught TypeError,如果您需要子树元素,则还需响应它们,就像我一样。 - Tami

55

这是我编写的插件,可以实现您所需的功能 - jquery.initialize

使用方式与使用.each函数相同,只需在元素上使用.initialize函数即可。与.each不同的是,它还会初始化将来添加的元素,而无需任何额外的代码-无论您是否使用AJAX或其他方式添加它们。

Initialize的语法与.each函数完全相同

$(".some-element").initialize( function(){
    $(this).css("color", "blue");
});

但现在如果页面上出现了新元素与.some-element选择器匹配,它将立即被初始化。添加新项的方式并不重要,你不需要关心任何回调等。

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

插件基于MutationObserver


非常感谢您。 - Brandon Harris
如何停止初始化?一旦我正在寻找的内容被添加,我想停止观看... - Evgeny Vostok
$(".some-element").initialize[..]被弃用了。请使用以下代码替代: $.initialize(".addDialog", function () { $('.noData').hide(); }); - Heimi
是否可以仅对动态添加的元素(例如通过Ajax)执行.initialize函数的回调,并跳过页面最初加载时已经存在的元素的回调?我正在尝试向options中添加一个参数来实现这一点,但到目前为止我还没有成功。 - Nevermore
你可以在 Github 上添加问题。目前,从技术上讲,你可以这样做:$('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback); - Adam Pietrasiak
这太棒了。谢谢! - MarkPraschan

28

在查看了这篇和几篇其他帖子后,我试图将我认为最好的方案提炼出来,以便能够检测到元素类别的插入,并对这些元素进行操作

function onElementInserted(containerSelector, elementSelector, callback) {

    var onMutationsObserved = function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes.length) {
                var elements = $(mutation.addedNodes).find(elementSelector);
                for (var i = 0, len = elements.length; i < len; i++) {
                    callback(elements[i]);
                }
            }
        });
    };

    var target = $(containerSelector)[0];
    var config = { childList: true, subtree: true };
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    var observer = new MutationObserver(onMutationsObserved);    
    observer.observe(target, config);

}

onElementInserted('body', '.myTargetElement', function(element) {
    console.log(element);
});

对我来说,重要的是,这使得目标元素能够存在于“addedNodes”中的任何深度,并且可以在最初插入元素时处理该元素(无需搜索整个文档设置或忽略“已处理”标志)。


1
你的条件应该是 if(mutation.addedNodes.length),因为在类型为 childListmutation 中,addedNodesremovedNodes 数组总是存在的(在 IE11、Chrome53、FF49 中已经检查过)。仍然给你一个加分,因为你是唯一一个考虑到了 addedNodes 的答案(只有在添加节点时才调用 callback);不仅在这个线程上,在许多其他线程上也是如此。其他人只是为所有变异调用 callback,即使节点被删除。 - Vivek Athalye
很好 - 运行得很棒。谢谢。 - Christopher

18

经过3年经验后,我如何听懂“DOM中添加某个类的元素”的说法:你只需要在jQuery的html()函数中添加一个钩子,就像这样:

function Start() {

   var OldHtml = window.jQuery.fn.html;

   window.jQuery.fn.html = function () {

     var EnhancedHtml = OldHtml.apply(this, arguments);

     if (arguments.length && EnhancedHtml.find('.MyClass').length) {

         var TheElementAdded = EnhancedHtml.find('.MyClass'); //there it is
     }

     return EnhancedHtml;
   }
}

$(Start);

如果你正在使用jQuery,那么这个方法适用于你。它不依赖于特定于浏览器的DOMNodeInserted事件,因此具有跨浏览器兼容性。我还为.prepend()添加了相同的实现。

总体来说,这对我非常有效,希望对您也是如此。


6
如果你只使用jQuery的html助手函数来修改DOM,而不是仅仅“使用jQuery”,那么这个方法才能生效。例如,使用jQuery.append添加的元素不会触发此事件。 - iameli
1
是的,这就是为什么我澄清了我使用 .preprend() 添加了相同的实现,如果你使用 .append(),那么也要用 .append() 做同样的事情。 - frenchie
有一种更快的方法,使用CSS3动画。这个开源库也使代码更简单:insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; }); - Dan Dascalescu
3
为什么会被踩?这实际上很有效:无需插件和不可靠的setTimeout函数。 - frenchie
2
点赞 - 这是一个有效的选项 - 为什么这个被踩了??????丹,CSS3动画不是XBC:http://caniuse.com/#search=keyframes; http://davidwalsh.name/detect-node-insertion。 - Cody

6
你可以使用变异事件。

http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mutationevents

编辑

来自MDN: https://developer.mozilla.org/zh-CN/docs/Web/Guide/Events/Mutation_events

已弃用 此功能已从Web中删除。虽然某些浏览器可能仍然支持它,但正在被废弃。不要在旧项目或新项目中使用它。使用它的页面或Web应用程序可能随时会出现问题。

变异观察者是DOM4中变异事件的拟议替代品。它们将包含在Firefox 14和Chrome 18中。

https://developer.mozilla.org/en/docs/Web/API/MutationObserver

MutationObserver 提供了一种方式让开发者对 DOM 的变动做出反应。它被设计为 DOM3 Events 规范中 Mutation Events 的替代品。
示例用法
下面的示例摘自http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/
// select the target node
var target = document.querySelector('#some-id');

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
  });    
});

// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };

// pass in the target node, as well as the observer options
observer.observe(target, config);

// later, you can stop observing
observer.disconnect();

2
突变事件根据mdn已经相当过时,现在应使用MutationObservers。 - Olga
现在IE10已经停止支持,可以基本确定MutationObserver得到了广泛的支持。 - rymo

1

对adeneo的回答做出两点补充:

(1) 我们可以更改该行

return el.classList.contains('MyClass');

if( el.classList ) {
    return el.classList.contains('MyClass');
} else {
    return false;
}

所以我们不会看到"Uncaught TypeError: Cannot read property 'contains' of undefined"错误。
(2) 在config中添加"subtree: true",以递归查找所有添加元素中的添加节点。

感谢您设置 subtree:true 并修复错误。 - Tami
1
@agrul,你能否提供完整的代码并标注出你所做的修改吗?这样回答会更易于使用。 - Jez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接