JavaScript DOM操作, 啥时候同步更新?

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .red {
            color: red;
        }

        .green {
            color: green;
        }
    </style>
</head>
<body>

<ul>
    <li class="red">哈哈0</li>
    <li class="red">哈哈1</li>
    <li class="red">哈哈2</li>
    <li class="red">哈哈3</li>
    <li class="red">哈哈4</li>
</ul>

<script>
    var liArr = document.getElementsByClassName('red');//一绿一红
    
    //    var liArr = document.querySelectorAll('.red');//全绿
    
    for (var i = 0; i < liArr.length; i++) {
        liArr[i].className = 'green';
    }
</script>
</body>
</html>

感谢各位指点,找到问题答案了.
后面是一个小结:
https://icbd.github.io/wiki/w...

阅读 4.9k
4 个回答

代码自上而下执行,照你的代码是一开始classNamered,执行到for循环的时候,更新classNamegreen

写成这样你就明白了

for (var i = 0, len = liArr.length; i < len; i++) {
    console.log('start:'+ JSON.stringify(liArr));
    liArr[0].className = 'green'; // 每次都改变第一个元素
    console.log('over:'+ JSON.stringify(liArr));
}

这是document.getElementsByClassName和document.querySelectorAll的返回值不同造成的,W3C规定:前者的返回值是一个动态的 Node List,后者返回的是一个静态的 Node List,类似于快照。https://www.zhihu.com/questio...

querySelectorAll返回的元素集合中的元素是非实时的,这就是这个问题最关键之处,也是与getElement...系列的最大区别。注意这个字眼“实时”。

1) 渲染引擎把HTML文档解析成DOM树,把标签转换成DOM节点。

2) Gecko(Firefox):在解析HTML文档的同时,渲染引擎会把文档内的样式和外部CSS文件解析成CSS规则树。而Webkit不会生成CSS规则树。
3) 渲染器是在文档解析和创建DOM节点后创建的,会计算DOM节点的样式信息。如果元素的display属性被设置成了none,或者如果元素的子孙继承了display:none,该元素的渲染器不会被创建。节点的子类和display属性一起决定为该节点创建什么样的渲染器。渲染器是渲染树的节点。

          Gecko(Firefox):DOM树和CSS规则树结合之后,形成了Style Content Tree。将Style Content Tree和渲染树关联,就完成了渲染树。

          渲染树包含多个视觉属性(如颜色和尺寸),属性的排列顺序是在屏幕上显示的顺序。

4) 渲染树构建出来之后,就进入布局阶段,为渲染树的每个节点分配一个屏幕上的确切坐标。

5) 最后一步,就是遍历渲染树,通过用户界面后端(UI Backend)将每个节点绘制出来。

注:这是一个渐进的过程。渲染引擎不会等到整个HTML文档解析完成之后,才开始构建渲染树和确定布局。在不断接收和处理网络层的请求内容时,渲染引擎会将部分内容解析并显示出来。

Dom更新涉及到的东西很多,不同的Dom操作会引起不同的页面变化,Repain 和 Reflow

Reflow 的成本比 Repaint 的成本高得多的多。DOM Tree 里的每个结点都会有 reflow 方法,一个结点的 reflow 很有可能导致子结点,甚至父点以及同级结点的 reflow。在一些高性能的电脑上也许还没什么,但是如果 reflow 发生在手机上,那么这个过程是非常痛苦和耗电的。

所以,下面这些动作有很大可能会是成本比较高的。

当你增加、删除、修改 DOM 结点时,会导致 Reflow 或 Repaint。
当你移动 DOM 的位置,或是搞个动画的时候。
当你修改 CSS 样式的时候。
当你 Resize 窗口的时候(移动端没有这个问题),或是滚动的时候。
当你修改网页的默认字体时。
注:display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,因为没有发现位置变化。

因为 document.getElementsByClassName 返回的是一个HTMLCollection类数组对象集合,这个集合是一个动态的集合,只要有一个节点发生变化,DOM都会重新渲染,liArr也会重新取值;
一开始的 liArr 是 [red0, red1, red2, red3, red4];
当 i = 0时,第一个变成green, 那么liArr就变成了[red1, red2, red3, red4], 原来的第二个red变成了第一个,依次类推,每个red向前移了一位;
当 i = 1时,这时的 liArr[1]即red2是最初的liArr中的liArr[2], red1被跳过了,所以颜色没变;
依次类推;

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题