重绘与回流
首先要了解页面是如何呈现的:
- HTML文档加载后生成DOM树(包括display:none;元素);
- 在DOM树的基础上配合css样式结构体生成render树(不包含display:none;、head节点,包含visibility:hidden;节点),即页面中的占位确定了.
- 最后绘制页面(也叫渲染),不会改变页面布局的一些属性:color、背景色等。
- 重绘(repaint):更新页面元素的属性引起的,如颜色、透明度等不会改变页面布局而需要重新渲染的。
- 回流(reflow):render树中部分或全部元素的尺寸、布局、隐藏等(内容、结构、位置)改变引起的,每个页面至少有一次回流(即初始构建页面时),成本较高。
- DOM操作(对元素的增删改、顺序变化等)
- 内容变化,包括表单区域内的文本变化
- css属性的更改或者重新计算
- 增删样式表内容
- 修改class属性
- 浏览器窗口变化(滚动或缩放)
- 伪类样式激活(:hover等)
这些情况会引起回流,减少回流就是尽量避免或减少这写操作的执行次数。
减少页面重绘与回流:
页面中需要需要动态生成id为box的div中的内容,结果如下:
<div id="box">
<p class="btn-title"><i class="iconfont"></i>项目阶段</p>
<ul class="btn-group" id="proStepul">
<li class="btn-item" data_id="">全部</li>
<li class="btn-item" data_id="">全部</li>
<li class="btn-item" data_id="">全部</li>
<li class="btn-item" data_id="">全部</li>
<li class="btn-item" data_id="">全部</li>
</ul>
</div>
一般的做法可以用innerHTML或者createElement:
var Obox = document.getElementById("box");
Obox.innerHTML = '<p class="btn-title"><i class="iconfont"></i>项目阶段</p>';
Obox.innerHTML += '<ul class="btn-group" id="proStepul">';
for(var i=0;i<5;i++){
Obox.innerHTML += '<li class="btn-item" data_id="">全部</li>';
}
Obox.innerHTML +='</ul>';
这样写要向Obox中写入很多次。成本增高,建议的方法是用变量存储然后一次写入。
var Obox = document.getElementById("box");
var str = '';
str = '<p class="btn-title"><i class="iconfont"></i>项目阶段</p>';
str += '<ul class="btn-group" id="proStepul">';
for(var i=0;i<5;i++){
str += '<li class="btn-item" data_id="">全部</li>';
}
str +='</ul>';
Obox.innerHTML = str;
用createElement方法如下:可是这样的结构很糟糕,多次写入,增加很多重绘与回流。
var Obox = document.getElementById("box");
var Op = document.createElement("p");
Obox.appendChild(Op);
Op.setAttribute("class","btn-title");
Op.innerHTML ="项目阶段";
var Oi = document.createElement("i");
Op.appendChild(Oi);
Oi.setAttribute("class","iconfont");
var Oul = document.createElement("ul");
Obox.appendChild(Oul);
Oul.setAttribute("class","btn-group");
Oul.setAttribute("id","proStepul");
for(var i=0;i<5;i++){
var Oli = document.createElement("li");
Oul.appendChild(Oli);
Oli.setAttribute("class","btn-item");
}
合理的方法是:先构建元素,然后设置元素的属性等,最后再将元素添加到页面相应位置:
var Obox = document.getElementById("box");
var Op = document.createElement("p");
//Obox.appendChild(Op);
Op.setAttribute("class","btn-title");
Op.innerHTML ="项目阶段";
var Oi = document.createElement("i");
Op.appendChild(Oi);
Oi.setAttribute("class","iconfont");
var Oul = document.createElement("ul");
//Obox.appendChild(Oul);
Oul.setAttribute("class","btn-group");
Oul.setAttribute("id","proStepul");
for(var i=0;i<5;i++){
var Oli = document.createElement("li");
Oul.appendChild(Oli);
Oli.setAttribute("class","btn-item");
}
Obox.appendChild(Op);
Obox.appendChild(Oul);
这里只需将新建元素写入页面的操作移到最后即可,至于元素在未写入页面之前的操作顺序如何,对页面并没有什么影响,不会造成页面回流或者重绘。
有一种情况为:需要像页面中写入多个同一级的元素,如下:向ul元素中写入多个li元素
<ul class="btn-group on" id="proStepul">
<li class="btn-item on" >全部</li>
<li class="btn-item on" >全部</li>
<li class="btn-item on" >全部</li>
<li class="btn-item on" >全部</li>
<li class="btn-item on" >全部</li>
</ul>
如果用之前的方法,不可避免的要写五个Oul.appendChild("Oul");了,这时我们可以用JS提供的可以整合多个碎片的中介----document.createDocumentFragment();
var Oul = document.getElementById("proStepul");
var obj = document.createDocumentFragment();
for (var i = 0; i < 5; i++) {
var Oli = document.createElement("li");
Oli.setAttribute("class", "btn-item");
obj.appendChild(Oli);
}
Oul.appendChild(obj);