本篇文章主要分享一下操做DOM時的一些細節,來提升頁面性能。 首先咱們來思考如下幾個問題。 1.如何獲取頁面中全部class爲div1和div2的div元素。 2.你瞭解HTMLCollection和NodeList嗎?有什麼區別? 3.如何獲取一個元素的css屬性值? 4.怎麼減小重排和重繪?
這個問題若是我之前寫的話,我會這麼寫。
var errs = [], divs = document.getElementsByTagName('div'), className = ''; for(var i = 0 , len = divs.length; i < len; i ++ ){ className = divs[i].className; if (className === 'div1' || className === 'div2') { errs.push(divs[i]); } }
看起來中規中矩,好像沒什麼大問題。 可是如今我會這麼寫
var errs2 = document.querySelectorAll('div.div1, div.div2');
用querySelectorAll,表面上看縮短了不少代碼量,其實它的做用還不止這個。接下來咱們引出第二個問題。
咱們先來看兩段代碼。想一下這兩段代碼有什麼區別。
// 片斷一 var all = document.getElementsByTagName('div'); for(var i = 0; i < all.length ; i ++) { document.body.appendChild(document.createElement('div')); } // 片斷二 var all2 = document.querySelectorAll('div'); for(var i = 0; i < all2.length ; i ++) { document.body.appendChild(document.createElement('div')); }
你能夠分別複製一下這兩段代碼在你的瀏覽器中運行,看看有什麼區別沒有。前提是你的頁面中至少有一個div。不然不會進入到循環中去。
運行完以後你會發現,片斷一運行完以後頁面崩潰了。片斷二正常如期運行。爲何呢?由於用getElementsByTagName方法獲取到的是HTMLCollection,而HTMLCollection是實時獲取的,也就是說,每次循環時請求all.length,都會從新去觸發一次getElementsByTagName方法。致使all.length每次循環以後都會加1。最終形成了死循環。而用querySelectorAll方法獲取到的結果是NodeList,NodeList不是實時獲取的。也正是由於這個緣由,咱們推薦使用querySelector去代替getElementBy方法。實時獲取會形成頁面不斷重排,這極大的下降了性能。另外:除了getElementByXXX方法外,document.images/document.links等方法返回的也是HTMLCollection。css
咱們知道,直接用div.style.color這樣的方式去獲取css樣式只能獲取到行內寫的屬性,寫在style標籤中的樣式是獲取不到的。標準瀏覽器中有一個window.getComputedStyle方法,能夠獲取實時的樣式(在IE中是currentStyle)。可是這個方法也會引發頁面的重排,由於只有把頁面重排以後他纔會獲取到實時的樣式。瀏覽器
在改變DOM的一些樣式時,重排和重繪是沒法避免的,可是咱們要儘可能讓這個次數下降,下面介紹集中方法。
1.當須要給一個DOM元素加各類動畫或者改變一系列樣式時,先把這個元素設置成絕對定位。由於脫離文檔流後,再對這個元素進行操做就不會影響到他周圍的元素,也就不會發生重排。等到一系列複雜的操做結束以後,再把這個元素的定位恢復到以前的樣子。這樣只進行了兩次重排,能明顯的提升性能。
2.當咱們須要改變不少樣式時,不要用style.color,style.width,style.height這樣一個一個去改,由於每次修改都會引發一次重排。咱們應該使用給這個元素加一個類名,把全部的樣式都寫到這個類名中。或者是改style.cssText。這樣能夠有效的減小重排的次數。
3.用fragment代替dom。思考這樣一個問題,如何給頁面中插入10000個div元素。每次建立一個而後append到body中去嗎?這顯然不是一個好的辦法。咱們應該先把全部建立的元素都添加到一個fragment中。而後再把fragment添加到body中去。這樣只會引發一次重排,而不是1萬次。
4.介紹一些會引發重排的屬性。當訪問元素的如下屬性時也會引發重排,由於這些屬性是實時算的。緩存
5.儘量少的訪問DOM。看下面兩段代碼。app
// 片斷一 console.time('test1') for(let i = 0; i < 1000; i++) { document.getElementById('div1').innerHTML+= i; } console.timeEnd('test1'); // 打印結果80ms左右 // 片斷二 console.time('test3') let count = '' for(let i = 0; i < 1000; i++) { count += i; } document.getElementById('div1').innerHTML+= count; console.timeEnd('test3'); // 打印結果0.3ms左右
一個是不停的訪問DOM,並修改內容,一個是先把內容緩存起來,只訪問一次。打印出來時間相差好幾百倍。dom
用正確的DOM屬性。性能
// 由於咱們大部分時候須要操做的是元素節點,像什麼文本節點,註釋節點通常都會過濾掉。若是是這樣的話,提倡用前面的屬性代替後面的屬性。 // children childNodes // childElementCount childNodes.length // firstElementChild firstChild // lastElementChild lastChild // nextElementSibling nextSibling // previousElementSibling previousSibling
須要給大量元素分發事件時,採起事件委託。由於瀏覽器不只不須要分發不少事件,並且須要跟蹤每一個事件處理器,這也會佔用更多內存。動畫