避免不必要回流操作的另外一種方法,就是在對(duì)DOM操作之前,把要操作的元素,先從當(dāng)前DOM結(jié)構(gòu)中刪除。對(duì)于刪除一個(gè)元素,基本有兩種方法:
- 通過removeChild()或者replaceChild()實(shí)現(xiàn)真正意義上的刪除。
- 設(shè)置該元素的display樣式為“none”。
而一旦修改操作完成,上面這個(gè)過程就需要反轉(zhuǎn)過來,將刪除的元素重新添加到當(dāng)前的DOM結(jié)構(gòu)中,我們還是拿上面的例子來做說明:
list.style.display = "none"; for (var i=0; i < items.length; i++){ var item = document.createElement("li"); item.appendChild(document.createTextNode("Option " + i); list.appendChild(item); } list.style.display = "";
將list的display樣式設(shè)置為“none”后,就將這個(gè)元素從當(dāng)前的DOM結(jié)構(gòu)中刪除了,因?yàn)檫@個(gè)節(jié)點(diǎn)不再可視。在將display屬性設(shè)置回之前的默認(rèn)值之前,向其下添加子元素是不會(huì)觸發(fā)回流操作的。
另外一個(gè)經(jīng)常引起回流操作的情況是通過style屬性對(duì)元素的外觀進(jìn)行修改。比如下面這個(gè)例子:
element.style.backgroundColor = "blue"; element.style.color = "red"; element.style.fontSize = "12em";
這段代碼修改了三個(gè)樣式,同時(shí)也就觸發(fā)了三次回流操作。每次修改元素的style屬性,都肯定會(huì)觸發(fā)回流操作。如果你要同時(shí)修改一個(gè)元素的很多樣式,最好的辦法是將這些樣式放到一個(gè)class下,然后直接修改元素的class,這可比單獨(dú)修改元素的樣式要強(qiáng)得多。比如下面這個(gè)例子:
.newStyle { background-color: blue; color: red; font-size: 12em; }
這樣我們?cè)贘avaScript代碼中,只需下面這行代碼就可以修改樣式:
/*element.className = "newStyle";*/
修改元素的class屬性,會(huì)一次將所有的樣式應(yīng)用在目標(biāo)元素上,而且只會(huì)觸發(fā)一次回流操作。這樣做不止更加有效,而且還更容易維護(hù)。
既然DOM幾乎在所有情況下都很慢,就很有必要將獲取的DOM數(shù)據(jù)緩存起來。這種方法,不僅對(duì)獲取那些會(huì)觸發(fā)回流操作的屬性(比如offsetWidth等)尤為重要,就算對(duì)于一般情況,也同樣適用。下面介紹一個(gè)效率低的夸張的例子:
document.getElementById("myDiv").style.left = document.getElementById("myDiv").offsetLeft + document.getElementById("myDiv").offsetWidth + "px";
這里對(duì)getElementById()調(diào)用了三次,是一個(gè)很大的問題,訪問DOM是很昂貴的,而這三個(gè)調(diào)用恰恰訪問的是同一個(gè)元素,也許我們像下面這樣寫,會(huì)更好一些:
var myDiv = document.getElementById("myDiv"); myDiv.style.left = myDiv.offsetLeft + myDiv.offsetWidth + "px";
我們?nèi)サ袅艘恍┤哂嗖僮,現(xiàn)在對(duì)DOM操作的次數(shù)已經(jīng)被減小了。對(duì)于那些使用次數(shù)超過一次的DOM值,我們都應(yīng)該緩沖起來,這樣可以避免無謂的性能消耗。
也許,拖慢屬性訪問速度的罪魁禍?zhǔn)拙褪荋TMLCollection對(duì)象。這些對(duì)象是object類型的,只要DOM需要返回一組節(jié)點(diǎn)時(shí)就會(huì)使用這個(gè)對(duì)象,也就是說childNodes屬性和getElementsByTagName()的返回值都屬于這種情況。我們可能經(jīng)常會(huì)將HTMLCollection當(dāng)作數(shù)組來使用,但實(shí)際上他是一個(gè)根據(jù)DOM結(jié)構(gòu)自動(dòng)變化的實(shí)體對(duì)象。每次你訪問一個(gè)HTMLCollection對(duì)象的屬性,他都會(huì)對(duì)DOM內(nèi)所有的節(jié)點(diǎn)進(jìn)行一次完整匹配,這意味著下面的代碼將導(dǎo)致一個(gè)死循環(huán):
var divs = document.getElementsByTagName("div"); for (var i=0; i < divs.length; i++){ //infinite loop document.body.appendChild(document.createElement("div")); }
這段代碼為什么會(huì)變成死循環(huán)呢?因?yàn)樵诿看窝h(huán)中,將會(huì)向document中新增一個(gè)div元素,同時(shí)也會(huì)更新divs這個(gè)集合,也就是說循環(huán)的索引永遠(yuǎn)都不會(huì)超過divs.length的值,因?yàn)閐ivs.length的值是伴隨著循環(huán)而遞增的。每次訪問divs.length,就會(huì)更新一次集合對(duì)象,這可比訪問一個(gè)普通數(shù)組的length屬性要付出更大的代價(jià)。當(dāng)對(duì)HTMLCollection對(duì)象進(jìn)行操作時(shí),應(yīng)該將訪問的次數(shù)盡可能的降至最低,最簡(jiǎn)單的,你可以將length屬性緩存在一個(gè)本地變量中,這樣就能大幅度的提高循環(huán)的效率。
var divs = document.getElementsByTagName("div"); for (var i=0, len=divs.length; i < len; i++){ //not an infinite loop document.body.appendChild(document.createElement("div")); }
修改后的代碼已經(jīng)不是死循環(huán)了,因?yàn)樵诿看窝h(huán)時(shí),len的值都是保持固定不變的。將屬性值緩存起來除了更加有效率,還可以保證document不會(huì)執(zhí)行多于一次的查詢。
本文是“Speed up your JavaScript”這個(gè)系列的最后一篇文章,我希望你現(xiàn)在已經(jīng)知道如何避免那個(gè)腳本失控的對(duì)話框,以及如何讓你的腳本運(yùn)行的更快。我所提到的技巧很多別人已經(jīng)提過了,我只是將它們組織到一起,這樣大家可以更容易的找到這些信息。如果你有什么更好的話題需要來我整理,在評(píng)論中直接告訴我,或者直接 聯(lián)系我 吧。
本文鏈接:http://www.95time.cn/tech/web/2009/6477.asp
出處:七月佑安
責(zé)任編輯:bluehearts
上一頁(yè) 提升JavaScript運(yùn)行速度之DOM篇 [1] 下一頁(yè)
◎進(jìn)入論壇網(wǎng)頁(yè)制作、WEB標(biāo)準(zhǔn)化版塊參加討論,我還想發(fā)表評(píng)論。
|