# 3. DOM编程
- 把DOM和ECMAscript各自想象成一个岛屿,它们之间用收费桥梁连接。ECMAscript每次访问DOM,都要途径这座桥,并交纳过桥费。访问DOM次数越多,费用也就越高。访问DOM的次数越多,代码的运行速度越慢。因此,最好的做法是:减少访问DOM的次数,把运算尽量留在ECMAscript这一端处理。
- 在旧版本浏览器中,innerHTML比原生DOM方法(document.createElement())快的多,但在基于webkit内核的浏览器中相反:用DOM方法略胜一筹。
- 节点克隆
var dupNode = node.cloneNode(deep);
// node
// 将要被克隆的节点
// dupNode
// 克隆生成的副本节点
// deep 可选
// 是否采用深度克隆,如果为true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身.
element.cloneNode(false)
element.cloneNode(true)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 读取一个集合的length比读取普通数组的length要慢的多,因为每次都要重新计算。
- 使用children替代childNodes会更快,因为集合项更少。HTML源码中的空白实际上是文本节点,而且它并不包含在children集合中。
# 重排(reflow)与重绘(repaint)
当DOM的变化影响了元素的几何属性(宽和高),浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程是重排。
重排后,浏览器会重新绘制受影响的部分到屏幕中,这个过程称为重绘。
# 重排何时发生
- 添加或删除可见的DOM元素。
- 元素位置改变。
- 元素尺寸改变(margin、padding、边框厚度、width、height等)
- 内容改变(文本改变或图片被另一个不同尺寸的图片替换)
- 页面渲染器初始化。
- 浏览器窗口尺寸改变。
- 滚动条出现会使整个页面重排。
# 渲染树变化的排队与刷新
大多数浏览器通过队列化修改并批量执行来优化重排过程。。获取布局信息的操作会导致队列刷新,以此来获取最新信息。
//getComputedStyle()(currentStyle in IE)
bodystyle.color='red'
tmp=computed.backgroundColor;
bodystyle.color='#fff'
tmp=computed.backgroundImage;
bodystyle.color='#000'
tmp=computed.backgroundSize;
1
2
3
4
5
6
7
2
3
4
5
6
7
以上代码每次获取样式属性都要刷新渲染队列并重排,重排了三次。可以改成下面这样只重排一次:
//getComputedStyle()(currentStyle in IE)
bodystyle.color='#fff'
bodystyle.color='#000'
bodystyle.color='red'
tmp=computed.backgroundImage;
tmp=computed.backgroundColor;
tmp=computed.backgroundSize;
1
2
3
4
5
6
7
2
3
4
5
6
7
为了减少发生次数,应该合并多次对DOM的样式的修改,然后一次处理掉。
//重排了三次
el.style.borderLeft='1px'
el.style.borderRight='1px'
el.style.padding='1px'
//重排了一次
el.style.cssText='border-left:1px;border-right:1px;'
1
2
3
4
5
6
2
3
4
5
6
# 批量修改DOM
可以通过以下步骤来减少重绘和重排的次数:
- 使文档脱离文档流。
- 对其应用多重改变。
- 把元素带回到文档中。
该过程会触发两次重排。
- 通过改变display属性临时从文档中移除元素,改变完后,再恢复它。
- 在文档之外创建并更新一个文档片断(fragment),然后把它附加到原始列表中。文档片断是一个轻量级的document对象。文档片断一个便利的语法特性是当你附加一个片断到节点中时,实际上被添加的是该片断的子节点,而不是片断本身。(推荐方案,因为DOM遍历最少,重排次数最少)
var fragment=document.createDocumentFragment()
appendDatatoElement(fragment,data)
el.appendChild(fragment)
//以上代码只触发了一次重排
1
2
3
4
2
3
4
3.为需要修改的节点创建一个备份,然后对副本进行操作,完成后使用新节点替换旧的节点。(el.cloneNode(true))
# 缓存布局信息
1.当你查询布局信息是,比如获取(offsets)、滚动位置、或计算出的样式值时,浏览器为了返回最新值,会刷新队列并应用所有改变。最好的做法是减少布局信息的获取次数,获取后把它赋值给局部变量,然后操作局部变量。
# 事件委托
- 当页面中存在大量元素,而且每一个都要一次或多次绑定事件处理器时,这种情况可能会影响性能。
- 可以使用事件委托。:事件逐层冒泡并能被父级元素捕获。使用事件代理,只需要给外层元素绑定一个处理器,就可以处理在其子元素上触发的所有事件。
- 根据DOM标准每个事件都要经历三个阶段:
- 捕获
- 到达目标
- 冒泡
IE不支持捕获。
可以根据e.target判断是哪个元素。
e.preventDafault();//阻止默认行为
e.stopPropagation();//取消冒泡
1
2
2
← 2. 数据存取 4. 算法和流程控制 →