由于JavaScript开发离不开DOM操作,所以对DOM操作的性能优化在Web开发中是非常重要的。Repaint(或称为Redraw)是一种不会影响当前DOM结构和布局的一种重绘动作。下面动作都会产生Repaint动作:
❑不可见到可见(visibility样式属性)。
❑颜色或图片变化(background、border-color、color样式属性)。
❑不改变页面元素大小、形状和位置,但改变其外观的变化。
Reflow主要发生在DOM树被操作的时候。任何改变DOM的结构和布局的操作都会产生Reflow。当一个元素的Reflow操作发生时,它的所有父元素和子元素都会产生Reflow,最后Reflow必然会导致Repaint的产生。例如,如下动作会产生Repaint动作:
❑浏览器窗口的变化。
❑DOM节点的添加和删除操作。
❑一些改变页面元素大小、形状和位置的操作的触发。
每次Reflow会比Repaint带来更多的资源消耗,应该尽量减少Reflow的发生,或者将其转化为只会触发Repaint操作的代码,例如:
var pDiv=document.createElement("p");
document.body.appendChild(pDiv);//Reflow
var cDiv1=document.createElement("p");
var cDiv2=document.createElement("p");
pDiv.appendChild(cDiv1);//Reflow
pDiv.appendChild(cDiv2);//Reflow
在上面代码中,总共产生3次Reflow。下面进行优化:
var pDiv=document.createElement("p");
var cDiv1=document.createElement("p");
var cDiv2=document.createElement("p");
pDiv.appendChild(cDiv1);
pDiv.appendChild(cDiv2);
document.body.appendChild(pDiv);//Reflow
这样只有一次Reflow,因此,推荐这种DOM节点操作的方式。
关于上述较少Reflow操作的解决方案,还有一种可以参考的模式:利用display减少Reflow。
var pDiv=document.getElementById("parent");
pDiv.style.display="none";//reflow
pDiv.appendChild(cDiv1);
pDiv.appendChild(cDiv2);
pDiv.appendChild(cDiv3);
pDiv.appendChild(cDiv4);
pDiv.appendChild(cDiv5);
pDiv.style.;
pDiv.style.;
pDiv.style.display="block";//reflow
先隐藏pDiv,再显示,这样隐藏和显示之间的操作便不会产生任何的Reflow,提高了效率。
DOM元素中有一些特殊的测量属性的访问和方法的调用,也会触发Reflow,比较典型的就是offsetWidth属性和getComputedStyle方法。例如,下面代码都会产生Reflow。
var width=element.offsetWidth;
var scrollleft=element.scrollleft;
var display=window.getComputerStyle(p,'').getPropertyValue('display');
这些测量属性和方法如下:
❑offsetLeft
❑offsetTop
❑offsetHeight
❑offsetWidth
❑scrollTop/Left/Width/Height
❑clientTop/Left/Width/Height
❑getComputedStyle
❑currentStyle(in IE)
对这些测量属性和方法的访问或调用都会触发Reflow的产生,应该尽量减少对这些属性和方法的访问或调用。
var pe=document.getElementById("pos_element");
var result=document.getElementById("result_element");
var pOffsetWidth=pe.offsetWidth;
result.children[0].style.width=pOffsetWidth;
result.children[1].style.width=pOffsetWidth;
result.children[2].style.width=pOffsetWidth;
在上面代码中,使用临时变量将offsetWidth的值缓存起来,这样就不用每次都访问offsetWidth属性。这种方式在循环中非常适用,可以极大地提高性能。
经常见到如下的代码:
var sElement=document.getElementById("pos_element");
sElement.style.border='1px solid red'
sElement.style.backgroundColor='silver'
sElement.style.padding='2px 3px'
sElement.style.marginLeft='5px'
从中可以看到,这里的每一个样式的改变,都会产生Reflow。要减少这种情况的发生,可以这样做:
.class1{
border:'1px solid red'
background-color:'silver'
padding:'2px 3px'
margin-left:'5px'
}
document.getElementById("pos_element").className='class1';
用class替代style,可以将原有的所有Reflow或Repaint的次数都缩减到一次。
var sElement=document.getElementById("pos_element");
var newStyle='border:1px solid red;'+'background-color:silver;'+'padding:2px 3px;'+"margin-left:5px;"
sElement.style.cssText+=newStyle;
一次性设置所有样式,也是减少Reflow、提高性能的方法。