【JavaScript】回流(reflow)与重绘(repaint)

重绘与回流

首先要了解页面是如何呈现的

  1. HTML文档加载后生成DOM树(包括display:none;元素);
  2. 在DOM树的基础上配合css样式结构体生成render树(不包含display:none;、head节点,包含visibility:hidden;节点),即页面中的占位确定了.
  3. 最后绘制页面(也叫渲染),不会改变页面布局的一些属性: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);
原文地址:https://www.cnblogs.com/fanshaokun/p/8117700.html