DOM突变观察者的DOM突变事件是否更慢? [英] Are DOM Mutation Observers slower than DOM Mutation Events?

查看:138
本文介绍了DOM突变观察者的DOM突变事件是否更慢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码使用DOM Mutation Event DOMNodeInserted 来检测正文元素的存在并将其 innerHTML 到包装器中。

 <!DOCTYPE html> 
< html lang =en>
< head>
< script src =http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js>< / script>
< script>
函数DOMmanipulation(){
if(document.body){
document.removeEventListener('DOMNodeInserted',DOMmanipulation);
// DOM操作启动
document.body.innerHTML ='< div class =wrapper>'+ document.body.innerHTML +'< / div>';
// DOM操作结束
}
}
document.addEventListener('DOMNodeInserted',DOM操作);
< / script>
< / head>
< body>
< p> Lorem ipsum dolor sit amet。< / p>
< / body>
< / html>

尽管包装成功,但是有一个错误显示没有找到一个节点。 这个答案的一个问题解释说,这是因为当jQuery加载时,它添加了一个 div 元素进入身体做一些测试,但是它没有删除那个 div 元素,因为该元素已经被包装到包装器中,所以它不是



上述实验告诉我们,$ j code> DOMNodeInserted 事件比jQuery的测试更快,因为jQuery的测试元素( div )在被jQuery删除之前被包装。











现在以下代码可以实现相同的操作,并且它正在使用新引入的DOM Mutation Observer。到目前为止(2012-07-11),它只适用于Chrome 18及更高版本。

 <!DOCTYPE html> ; 
< html lang =en>
< head>
< script src =http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js>< / script>
< script>
var observer = new WebKitMutationObserver(function(){
if(document.body){
observer.disconnect();
// DOM操作启动
文档。 body.innerHTML ='< div class =wrapper>'+ document.body.innerHTML +'< / div>';
// DOM操作结束
}
} );
observer.observe(document,{subtree:true,childList:true});
< / script>
< / head>
< body>
< p> Lorem ipsum dolor sit amet。< / p>
< / body>
< / html>

此代码没有产生任何错误。这意味着jQuery比DOM Mutation Observer更快,所以它可以在元素被包装到包装器之前删除它的测试元素( div )。












从上述两个实验中,我们发现,当涉及执行速度时:




  • DOM突变事件> jQuery的测试

  • jQuery的测试> DOM突变观察者



此结果是否可以适当地证明DOM突变观察者比DOM突变事件更慢?

解决方案

DOM突变观察者不是要比DOM突变事件更快。相反,它们旨在更有效率和更安全。



差异的基本要点是每当有变化时,DOM Mutation Events就会触发。所以这个代码例如将创建一个回调循环,最终会使浏览器崩溃。

  document.addEventListener('DOMNodeInserted' function(){
var newEl = document.createElement('div');
document.body.appendChild(newEl);
});

以这种方式调用的事实,往往也对浏览器有重大影响,因为它强制浏览器重新计算样式,回流和重绘循环之间的中断,或者更糟的是,强制浏览器重新计算样式,在每次回调时重新绘制和重绘。这个问题进一步令人厌烦,其他代码可能会执行,进一步改变DOM,这将继续被你的回调中断。



更重要的是因为事件以与普通DOM事件相同的方式传播,您将开始听到您可能不关心或未在代码中解释的元素的更改。所以DOM Mutation事件的整个机制可能会变得很麻烦来管理得很快。



DOM突变观察者通过按照名义建议观察DOM的更改并为您提供一份从改变。这是一个更好的情况,因为它允许浏览器在有意义的时间通知您,例如当文档空闲时,所有其他可能进行更改的JavaScript已经完成执行,或者在浏览器重新启动之前重新计算/重绘周期,因此它可以应用您所做的任何更改,而不必在不久之后重复循环。



它还使您更容易管理,因为您可以扫描所有更改的元素以找到所需的内容,而不是编写大量的案例处理代码为您不关心的东西,突变事件的情况也是如此。更重要的是,它只会称之为一次,所以您不必担心任何进一步的变化将会影响元素,即它们不再处于变化状态,它们已经改变。



所以在回答你的问题时,DOM Mutation Observers更慢,因为在通知你jQuery改变之前,他们等待jQuery完成对DOM的操纵。由于上述原因和您的示例,证明它是更安全的更有效的解决方案(您不再会导致错误),并且您并没有真正关心jQuery向DOM添加了一些东西,因为它将在之后删除它。观察者您将收到一份详细说明要添加和删除的jQuery元素的报告。



但这仍然有点麻烦,因为您必须弄清楚匹配中发生了什么所有的变化都发生了。现实是,就你所关心的事情没有发生(同样的元素被添加和删除),所以在DOM的结构中没有任何改变。为了帮助这个,有一个名为MutationSummary的小图书馆:



http://code.google.com/p/mutation-summary/



计算更改的净效果,仅调用您的回调传递那些变化。所以在你的情况下,你的回调根本就不会被调用,因为变化的净影响是零。



例如。对于以下你将只会得到一个改变。身体风格改为左:1000px。即使我改变了1000增量。变化的净效应只是其初始值与最终值之间的差异。

  function moveBody(){$ b $ var for(var i = 0; i <1000; i ++)document.body.style.left = i +'px'; 
}
moveBody();


The following code utilize DOM Mutation Event DOMNodeInserted to detect the existence of the body element and wrap its innerHTML into a wrapper.

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
        function DOMmanipulation() {
            if (document.body) {
                document.removeEventListener('DOMNodeInserted', DOMmanipulation);
                // DOM manipulation start
                document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>';
                // DOM manipulation end
            }
        }
        document.addEventListener('DOMNodeInserted', DOMmanipulation);
    </script>
</head>
<body>
    <p>Lorem ipsum dolor sit amet.</p>
</body>
</html>

And despite the success of the wrapping, there is an error shows that a node was not found. This answer of a question explained that it is because when jQuery had been loaded, it added a div element into the body to do some tests, but it failed to remove that div element because that element has been wrapped into the wrapper so that it's not a child element of body anymore.

The above experiment tells us that DOMNodeInserted event is faster than jQuery's tests because jQuery's test element (div) got wrapped before it can be removed by jQuery.




Now the following code can achieve the same manipulation, and it's using the newly introduced DOM Mutation Observers. As of this time (2012-07-11), it works only on Chrome 18 and higher.

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
        var observer = new WebKitMutationObserver(function() {  
            if (document.body) {
                observer.disconnect();
                // DOM manipulation start
                document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>';
                // DOM manipulation end
            }
        });
        observer.observe(document, { subtree: true, childList: true });
    </script>
</head>
<body>
    <p>Lorem ipsum dolor sit amet.</p>
</body>
</html>

This codes didn't produce any error. That means jQuery is faster than DOM Mutation Observers, so it was able to remove its test element (div) before that element can be wrapped into the wrapper.




From the above two experiments, we find that when it comes to execution speed:

  • DOM Mutation Events > jQuery's tests
  • jQuery's tests > DOM Mutation Observers

Can this result appropriately prove that DOM Mutation Observers is slower than DOM Mutation Events?

解决方案

DOM Mutation Observers, are not intended to be faster than DOM Mutation Events. Rather they are intended to be more efficient and safer.

The basic gist of the difference is that DOM Mutation Events fire whenever there is a change. So this code for example would create a callback loop, that will ultimately crash the browser.

document.addEventListener('DOMNodeInserted', function() {
    var newEl = document.createElement('div');
    document.body.appendChild(newEl);
});

The fact that they are called in this fashion and so often also has a significant effect on the browser, as it forces an interrupt between the browsers recalculate style, reflow and repaint cycle or worse forces the browser to recalculate styles, reflow and repaint on every callback. The problem is further exasperated by the fact that other code maybe executing that makes further changes to the DOM, which will continue to be interrupted by your callback.

What's more is that because events propagate in the same way as normal DOM Events, you're going to start hearing changes on elements that you might not care about or didn't account for in your code. So the whole mechanism of DOM Mutation Events can become troublesome to manage fairly quickly.

DOM Mutation Observers counteract these problems by, as the name suggests observing changes to the DOM and providing you with a report of all the changes that took place from from the start of the change. This is a much better situation to be in as it allows the browsers to notify you at a time that makes sense, for example when the document is idle and all other JavaScript that could make further changes has finished executing, or before the browser restarts the recalc / repaint cycle, so it can apply any changes you make, without having to repeat the cycle shortly after.

It also makes it easier for you to manage, because you can scan through all the changed elements to find what you're looking for, instead of writing lots of case handling code for stuff you don't care about, as was the situation with Mutation Events. And more importantly its only going to call it once, so you don't need to worry that any further changes are going to effect the elements i.e. they are no longer in changing state, they have changed.

So in answer to your question, DOM Mutation Observers are slower because they waited for jQuery to finish its manipulation of the DOM before it notified you of what jQuery changed. Which for the reason explained above and your example, proves it is safer more efficient solution ( you no longer cause an error), and you didn't really care that jQuery added something to the DOM because it would have removed it shortly after. With Observers you would have received a report detailing the jQuery element being added and removed.

This is still a bit troublesome however because you have to figure out what actually happened by matching up elements with all the changes that took place. The reality is that as far as you're concerned nothing happened ( the same element was added and removed ) so nothing has actually changed in the structure of the DOM. To help with this there is a little library called MutationSummary:

http://code.google.com/p/mutation-summary/

That calculates the net effect of the changes and only calls your callback passing in those changes. So in your case your callback would not have been called at all, because the net effect of the change was zero.

E.g. for the following you will only get one change. The body style was changed to left: 1000px. Even though I changed it in 1000 increments. The net effect of the change is only the difference between its initial value and its final one.

function moveBody() {
    for (var i = 0; i < 1000; i++) document.body.style.left = i + 'px';
}
moveBody();

这篇关于DOM突变观察者的DOM突变事件是否更慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆