如何减少Dom操作中的回流和重绘问题
什么是回流和重绘?
(1)当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。然而,每一个页面至少需要回流一次,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受影响的一部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程为重绘。
(2)当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color,这就是重绘。*注意:回流必然会引起重绘,而重绘不一定会引起回流。
常见的重绘和回流操作?
(1)添加、删除元素(回流+重绘)
(2)隐藏元素,display:none(回流+重绘)
(3)移除元素,比如改变top,left(jQuery的animate方法就是,该变top,left不一定会回流),或者移动元素到另外1个父元素中。(回流+重绘)
(4)对style的操作(对不同的属性操作,影响不一样)
(5)以下属性,只要改动他们的值,就会造成回流,这些属性包括:offsetLeft、offsetTop、offsetWidth、offsetHeight;scrollTop、scrollLeft、scrollWidth、scrollHeight;clientWidth、clientHeight、clientLeft、clientTop;
(6)还有一种是用户的操作,比如改变浏览器大小,改变浏览器的字体大小。
如何减少回流和重绘
减少回流、重绘其实就是需要减少对 render tree的操作(合并多次Dom和样式的修改),并减少对一下style信息的请求,尽量利用好浏览器的优化策略。具体方法如下:
1.直接改变className,如果动态改变样式,则使用cssText(考虑没有优化的浏览器)
// 不好的写法
var left = 1;
var top = 1;
el.style.left = left + "px";
el.style.top = top + "px";
// 比较好的写法
el.className += " className1";
// 比较好的写法
el.style.cssText += ";
left: " + left + "px;
top: " + top + "px;";
2. 让要操作的元素进行”离线处理”,处理完后一起更新
a) 使用DocumentFragment进行缓存操作,引发一次回流和重绘;
//不好的写法(模式中所说的反模式)
var p, t;
p = document.creatElement('p');
t = document.creatTextNode('fist paragraph');
p.appendChild(t);document.body.appendChild(p); //将引起一次回流
p = document.creatElement('p');
t = document.creatTextNode('second paragraph');
p.appendChild(t);document.body.appendChild(p); //将再引起一次回流
//好的写法
var p, t, frag;
frag = document.creatDocumentFragment();
p = document.creatElement('p');
t = document.creatTextNode('fist paragraph');
p.appendChild(t);
farg.appendChild(p);
p = document.creatElement('p');
t = document.creatTextNode('second paragraph');
p.appendChild(t);
farg.appendChild(p);
document.body.appendChild(frag); //相比前面的方法,这里仅仅引起一次回流,倘若页面里有很多这样的操作,利用文档随便将会提升很多
b) 使用display:none技术,只引发两次回流和重绘; ( 只是减少重绘和回流的次数,display:none 是会引起重绘并回流,相对来说,visibility: hidden只会引起重绘 )
c) 使用cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;
//建立克隆镜像var oldNode = document.getElementById('target'), clone = oldNode.cloneNode(true); //深复制// 处理克隆对象的操作....//完成后oldNode.parentNode.replaceChild(clone,oldNode);
3.不要经常访问会引起浏览器flush队列的属性,如果你确实要访问,利用缓存
//BAD WAYfor(循环)
{el.style.left = el.offsetLeft + 5 + "px";el.style.top = el.offsetTop + 5 + "px";} // 这样写好点
var left = el.offsetLeft,top = el.offsetTop,s = el.style;
for (循环) { left += 10; top += 10; s.left = left + "px"; s.top = top + "px"; }