DOM樹:表示頁面結(jié)構(gòu)。
渲染樹:表示DOM節(jié)點(diǎn)如何顯示。
定義
當(dāng)DOM元素影響了元素的幾何屬性(例如寬和高),瀏覽器需要重新計(jì)算元素的幾何屬性,同樣其它元素的幾何屬性也會和位置也會因此受到影響。瀏覽器會使渲染樹中受到影響的部分失效,并重新構(gòu)造渲染樹。這個(gè)過程稱為“重排”。
完成重排后,瀏覽器會重新繪制受影響的部分到屏幕上中,該過程稱為“重繪”。
- 重排發(fā)生的情況:
- 添加或刪除可見的DOM元素。
- 元素位置改變。
- 元素的尺寸改變(包括:內(nèi)外邊距、邊框厚度、寬度、高度等屬性的改變)。
- 內(nèi)容改變。
- 頁面渲染器初始化。
- 瀏覽器窗口尺寸改變。
- 重繪發(fā)生的情況:
重繪發(fā)生在元素的可見的外觀被改變,但并沒有影響到布局的時(shí)候。比如,僅修改DOM元素的字體顏色(只有Repaint,因?yàn)椴恍枰{(diào)整布局)
優(yōu)化辦法
-
fragment元素的應(yīng)用
<ul id='fruit'>
<li> apple </li>
<li> orange </li>
</ul>
如果代碼中要添加內(nèi)容為peach、watermelon兩個(gè)選項(xiàng),你會怎么做?
傳統(tǒng)做法:
var lis = document.getElementById('fruit');
var li = document.createElement('li');
li.innerHTML = 'peach';
lis.appendChild(li);var li = document.createElement('li'); li.innerHTML = 'watermelon'; lis.appendChild(li);
很容易想到如上代碼,但是很顯然,重排了兩次。隱藏的元素不在渲染樹中,太棒了,我們可以先把id為fruit的ul元素隱藏,然后添加li元素,最后再顯示,但是實(shí)際操作中可能會出現(xiàn)閃動,原因這也很容易理解。這時(shí),fragment元素就有了用武之地了。
var fragment = document.createDocumentFragment();
var li = document.createElement('li');
li.innerHTML = 'peach';
fragment.appendChild(li);
var li = document.createElement('li');
li.innerHTML = 'watermelon';
fragment.appendChild(li);
document.getElementById('fruit').appendChild(fragment);
文檔片段的一個(gè)便利的語法特性是當(dāng)你附加一個(gè)片斷到節(jié)點(diǎn)時(shí),實(shí)際上被添加的是該片斷的子節(jié)點(diǎn),而不是片斷本身。只觸發(fā)了一次重排,而且只訪問了一次實(shí)時(shí)的DOM。
- 緩存布局信息
例如myElement元素沿對角線移動,每次移動一個(gè)像素。到500*500像素的位置結(jié)束。timeout循環(huán)體中可以這么做
myElement.style.left = 1 + myElement.offsetLeft + 'px';
myElement.style.top = 1 + myElement.offsetTop + 'px';
if(myElement.offsetLeft >= 500){
stopAnimation();
}
顯然這種方法低效,每次移動都要查詢偏移量,導(dǎo)致瀏覽器刷新渲染隊(duì)列而不利于優(yōu)化。好的辦法是獲取一次起始位置的值,然后賦值給一個(gè)變量。如下
var current = myElement.offsetLeft;
current++;
myElement.style.left = current + 'px';
myElement.style.top = current + 'px';
if(myElement.offsetLeft >= 500){
stopAnimation();
} - 讓元素脫離文檔流
使用絕對位置定位頁面上的動畫元素,將其脫離文檔流。觸發(fā)一小區(qū)域的重繪。