2012-07-11 104 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