2012-07-11 52 views
8

以下代码利用DOM突变事件DOMNodeInserted来检测body元素的存在并将其innerHTML包装为包装。DOM突变观察者是否比DOM突变事件慢?

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

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

上面的实验告诉我们,DOMNodeInserted事件比jQuery的测试要快,因为jQuery的测试元素(div)在被jQuery删除之前被包装了。




现在下面的代码可以实现同样的操作,并且它使用新引进的DOM突变观察。截至目前(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 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> 

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




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

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

这个结果能否适当地证明DOM突变观察者比DOM突变事件慢?

回答

24

DOM突变观察者,并不打算比DOM突变事件快。相反,他们打算更高效和更安全。

差异的基本要点是每当有变化时DOM突变事件就会触发。所以这段代码会创建一个回调循环,最终会导致浏览器崩溃。

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

他们称这种方式,因此往往也对浏览器的一个显著的效果,因为它迫使浏览器之间的中断的事实重新计算风格,回流和重绘周期或更糟强制浏览器重新计算样式,回流和每个回调重绘。这个问题由于其他代码可能正在执行而进一步恶化,这会对DOM做进一步的修改,这将继续被你的回调中断。

更重要的是,因为事件以与常规DOM事件相同的方式传播,所以您将开始听到您可能不关心或未在代码中进行说明的元素更改。因此,DOM突变事件的整个机制可能会变得很麻烦,很难进行快速管理。

DOM变异观察者正如名称所示,观察对DOM的更改并向您提供关于从变更开始发生的所有变更的报告,从而抵消这些问题。这是一个更好的情况,因为它允许浏览器在有意义的时间通知您,例如当文档空闲并且所有其他可能进行进一步更改的JavaScript完成执行时,或者在浏览器重新启动之前重新计算/重新绘制周期,因此它可以应用您所做的任何更改,而不必在之后重复循环。

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

因此,在回答您的问题时,DOM Mutation Observers速度较慢,因为他们在通知您jQuery发生了哪些变化之前等待jQuery完成其对DOM的操作。由于上面解释的原因和你的例子,证明它是更安全的更有效的解决方案(你不再导致错误),并且你并不在意jQuery向DOM添加了什么东西,因为它会在不久之后删除它。通过观察者,您将收到一份报告,详细介绍正在添加和删除的jQuery元素。

然而,这仍然有点麻烦,因为您必须通过将元素与发生的所有更改进行匹配来找出实际发生的情况。实际情况是,就你所关心的事情而言,什么都没有发生(相同的元素被添加和删除),所以DOM的结构中没有任何变化。为了解决这个问题有一个叫做MutationSummary小图书馆:

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

计算的变化的净影响只有调用回调函数传递这些变化。所以在你的情况下,你的回调根本不会被调用,因为改变的净效果为零。

E.g.对于以下你只会得到一个改变。正文样式已更改为左侧:1000px。尽管我以1000的增量更改了它。变化的净效果只是其初始值和最终值之间的差异。

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

非常感谢@AshHeskes和很好的解释!最后我了解他们的执行原则。 – 2012-07-31 04:45:50

6

简单的答案是突变观察者是异步的。无论发动机是什么感觉,发生变化后的一段时间,在某些情况下发生了很多变化之后,都会发送它们。 DOM突变事件监听器可能会通知您很长时间,但同时引擎可以自由地完成其工作,而无需为每个琐碎的DOM更改不断创建和冒泡事件。

+0

谢谢Nartowicz! – 2012-10-10 00:25:21