Skip to content

DOM

遍历

  • 关系:父子、相邻,首尾

  • 区分 Element (Node) 和 Node,node 包含了 textNode、commentNode、空 等

    childNodes vs children(Element)

  • 集合,类数组

    for of Array.from

性能

简述,为什么 DOM 操作影响性能? 因为某些情况 NodeList 对象是动态的,每次访问 NodeList 对象,都会运行一次查询。

理解 DOM 的关键,就是理解 DOM 对性能的影响。DOM 操作往往是 JavaScript 程序中开销最大的部分,而因访问 NodeList 导致的问题 为最多。NodeList 对象都是“动态的”,这就意味着每次访问 NodeList 对象,都会运行一次查询。所以应减少对 NodeList 的访问,对 DOM 的操作。

https://developer.mozilla.org/en-US/docs/Web/API/NodeList

优化

document.createDocumentFragment DocumentFragment - 简书

  • 文档片段,不属于文档树,用于占位
  • 继承了 Node 方法

API

常用方法:

js
document.write
document.writeln // 换行
div.getAttribute('title') // 或者 div.title

[ele].insertAdjacentHTML([position], [html]) 邻接插入,比 innerHTML 效率高

脚本

  • 问题:当用户鼠标移动够快时,积累在 setTimeout 队列里的事件会导致动画效果产生滞后。 解决:将目标元素与事件绑定,以便清除。
js
function moveElement (elementID, final_x, final_y, interval) {
    ...
    var elem = document.getElementById(elementID);
    if (elem.movement) {
        clearTimeout(elem.movement);
    }
    ...
    var repeat = "moveElement('"+elementID+"', '"+final_x+"', '"+final_y+"', '"+interval+"')";
    // element.property = value
    elem.movement = setTimeout(repeat, interval);
}
  • 步进为 1px 时太慢了,如何解决?
js
// 每次向目标位置移动1px,执行条件是尚未到达
if (xpos < final_x) {
  xpos++
}
  • 根据距离提高步进,每次前进距离的十分之一,距离小于 10px 时,dist 向上取整,按 1px 步进。
js
dist = Math.ceil((final_x - xpos) / 10)
xpos = xpos + dist

注意点

  • focus blur 事件只会在 input textaera select 等键盘元素上发生

重绘和回流

重绘

样式变化,浏览器重新绘制样式

paint 样式

回流

尺寸、位置、属性变化,浏览器重新渲染部分或全部

flow 布局 性能影响更大

DOM Performance (Reflow & Repaint) (Summary)

优化

  • 合并操作 class/documentFragment
  • 用重绘属性代替回流属性
    • transform 代替 top/left
    • opacity 替代 visibility
  • 多个 DOM 统一操作(虽然V8会有缓存优化)
  • 先将 DOM 离线,即display:none;修改后显示
  • 不要把 DOM 放在已给循环中作为循环变量
  • 不要使用 table

样式计算

innerHTML

当读取innerHTML时,HTML 已经被解析为 DOM 并丢弃,标签会被转换为小写,有的浏览器还不一样,因此拿不到原始的 HTML

tag 标签只用小写

多个词用短杠连接,大小写不敏感,内部会转为小写?为了保证转换一致性,最好用小写。Vue 的组件在 HTML 中用的都是小写

观察 DOM 更新

DOM- MutationObserver MutationObserver Example

vue - update

页面插入 dom 几种方式

  • createElement
    html
    const div = document.createElement('div') 
    div.innerHTML = `
    <div id="global-page-close" onclick="window.close()">
      <span></span>
      <span></span>
    </div>
    ` 
    document.body.appendChild(div)
  • document.createDocumentFragment 不支持 innerHTML

es6-articles/13 - Creating HTML fragments with Template Literals.md at master · wesbos/es6-articles

DOM 选择

节点关系示意图

参考: Plain JavaScript - Functions and HelpersDOM Enlightenment - Exploring the relationship between JavaScript and the modern HTML DOM

页面加载过程(输入 url 到加载出页面)

a. 请求过程:

1. [URL=>DNS=>IP] 浏览器根据 DNS 服务器解析得到域名的 IP 地址
2. [Client-Req-Server] 向这个 IP 的机器发送 HTTP 请求
3. [Server-Res] 服务器收到、处理并返回 HTTP 请求
4. [Res] 浏览器得到返回内容

b. 渲染过程:

1. [DOM树] 根据 HTML 结构生成 DOM 树
2. [CSSOM] 根据 CSS 生成 CSSOM
3. [渲染树] 将 DOM 和 CSSOM 整合形成 RenderTree
4. [绘制] 根据 RenderTree 开始渲染和展示
5. [链接] 加载执行外链资源

DOM 树 和 渲染树 的区别: DOM 树与 HTMl 标签一一对应,而渲染树不包含 head 和隐藏元素 display none

CSS 加载不会阻塞 DOM 的解析(并行各自解析),但会阻塞后续 DOM 渲染(防止样式闪烁)

浏览器的渲染过程,DOM 树和渲染树的区别?_Fairy的博客-CSDN博客