用于创建新元素的 Mutation Observer

Posted

技术标签:

【中文标题】用于创建新元素的 Mutation Observer【英文标题】:Mutation Observer for creating new elements 【发布时间】:2012-10-27 22:49:49 【问题描述】:

我正在尝试在创建特定 div 时关闭某个功能。用最简单的话来说,我有这样的事情:

<a href="" id="foo">Click me!</a>
<script>
$("#foo").live("click",function(e) 
    e.preventDefault();
    $(this).append($("<div />").html("new div").attr("id","bar"));
);
</script>

之前,我让突变事件监听 div#bar 的创建 - 像这样:

$("#bar").live("DOMNodeInserted", function(event) 
    console.log("a new div has been appended to the page");
);

是否有使用 Mutation Observers 的等价物?我尝试了 Can you have a javascript hook trigger after a DOM element's style object changes? 上的 attrchange.js,但该插件仅检测元素何时被修改,而不是何时创建。

【问题讨论】:

只需使用自定义事件:$( this ).append( ... ).trigger( 'newchild' );,然后是$( '#bar' ).on( 'newchild', function () ... ); 演示: jsfiddle.net/u3dDk/1 抱歉 Sime Vidas,自定义事件对我不起作用。在我的示例中,我给出了我实际尝试做的一个非常简化的版本。我认为这比粘贴 500 行 JavaScript 更容易。本质上,我需要监听 div#bar 的创建的 JS 与 a#bar 的 click 函数完全分开 变异观察者可以做到这一点,但它们没有在 IE 中实现。 【参考方案1】:

使用jQuery时,MutationObservers的用法可以简化如下。

$("#btnAddDirectly").click(function () 
    $("#canvas").append($('<span class="stuff">new child direct</span>'));
);
$("#btnAddAsChildOfATree").click(function () 
    $("#canvas").append($('<div><div><span class="stuff">new child tree</span></div></div>'));
);

var obs = new MutationObserver(function(mutations, observer) 
  // using jQuery to optimize code
  $.each(mutations, function (i, mutation) 
    var addedNodes = $(mutation.addedNodes);
    var selector = "span.stuff"
    var filteredEls = addedNodes.find(selector).addBack(selector); // finds either added alone or as tree
    filteredEls.each(function ()  // can use jQuery select to filter addedNodes
      alert('Insertion detected: ' + $(this).text());
    );
  );
);

var canvasElement = $("#canvas")[0];
obs.observe(canvasElement, childList: true, subtree: true);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="canvas">
Canvas
</div>

<button id="btnAddDirectly">Add span directly to canvas</button>
<button id="btnAddAsChildOfATree">Add span as child of a tree</button>

别忘了,.observe() 的第二个参数 MutationObserverInit 很重要:

在选项中,如果 span 仅作为直接子级添加,则使用 childList: truesubTree: true 如果它可以在低于#canvas 的任何级别。

来自docs:

childList:如果要观察目标节点的子元素(包括文本节点)的添加和删除,则设置为 truesubtree:如果要观察目标和目标后代的突变,则设置为 true

【讨论】:

【参考方案2】:

变异观察者

// Select the node that will be observed for mutations
const targetNode = document.getElementById('some-id');

// Options for the observer (which mutations to observe)
const config =  attributes: true, childList: true, subtree: true ;

// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) 
    // Use traditional 'for loops' for IE 11
    for(const mutation of mutationsList) 
        if (mutation.type === 'childList') 
            console.log('A child node has been added or removed.');
        
        else if (mutation.type === 'attributes') 
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        
    
;

// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

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

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

【讨论】:

【参考方案3】:

这段代码会监听#foo 的子列表中的突变,并检查是否添加了id 为bar 的子列表。

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

$("#foo").live("click",function(e) 
    e.preventDefault();
    $(this).append($("<div />").html("new div").attr("id","bar"));
);

// define a new observer
var obs = new MutationObserver(function(mutations, observer) 
    // look through all mutations that just occured
    for(var i=0; i<mutations.length; ++i) 
        // look through all added nodes of this mutation
        for(var j=0; j<mutations[i].addedNodes.length; ++j) 
            // was a child added with ID of 'bar'?
            if(mutations[i].addedNodes[j].id == "bar") 
                console.log("bar was added!");
            
        
    
);

// have the observer observe foo for changes in children
obs.observe($("#foo").get(0), 
  childList: true
);

但是,这只观察到#foo。如果您想寻找添加#bar 作为其他节点的新子节点,则需要通过额外调用obs.observe() 来观察那些潜在的父节点。要观察 id 为 baz 的节点,您可以这样做:

obs.observe($('#baz').get(0), 
  childList: true,
  subtree: true
);

添加subtree 选项意味着观察者将寻找添加的#bar 作为子更深的后代(例如孙)。

【讨论】:

仅适用于 Chrome/Safari 的解决方案几乎不是合法的解决方案。 @ŠimeVidas 对不起,是的,我的错误;我有供应商前缀,因为我在 Chrome 中进行测试。我现在已经规范了供应商前缀,否则它应该可以正常工作。它是 Firefox 中的 MutationObserver 和 Webkit 中的 WebKitMutationObserver,但它们的工作方式相同。 Fiddle demo. @JimmyHa 这适用于 Firefox 和 Webkit 浏览器。 Opera 和 IE 还不支持 Mutation Observers,因此您需要使用 DOM3 突变事件之类的后备选项(或等待很长时间让 DOM4 方法达到实现成熟度)。 @apsillers - 谢谢!我修改了你的 js 以适应我的需要。我不是在寻找 id,而是在寻找一个特定的类:if ($(mutations[i].addedNodes[j]).hasClass("new_div_class")) 我会在我的票中说,如果用户想要我在 IE 中尝试做的同样功能丰富的事情,他们将不得不添加项目范围并支付给我更多。我们确定突变观察者比突变事件更好吗?似乎要编写更多行脚本来做同样的事情。 我相信观察者的主要优势是能够准确地缩小你想要什么样的突变事件以及你对DOM的哪一部分感兴趣。有了突变事件,每次您进行更改时,浏览器都必须遍历潜在的巨大 DOM 树。通过观察者,您可以通过选择不查看子树等来缩小您的兴趣范围。如果您有兴趣了解具体优势,请参阅this post。

以上是关于用于创建新元素的 Mutation Observer的主要内容,如果未能解决你的问题,请参考以下文章

尝试通过 createUser Mutation 创建新用户时出现 400 错误

使用函数生成器将值返回到 GraphQL Mutation 中的 outputFields

Apollo Client 2:来自 Mutation 的新对象:更新所有缓存的查询,这个新对象应该是其中的一部分? [复制]

可拖动的 jQuery UI 不适用于新创建的 DOM 元素

Mutation Observer 无法检测到元素的 dom 移除

删除数组中的第一个和最后一个元素