DOM是一个独立于语言的,用于操作XML和HTML文档的程序接口,尽管DOM是个独立语言的API,但它在浏览器中的接口却是用JavaScript实现的。浏览器通常会把DOM和JavaScript独立实现,例如Chrome的DOM渲染使用的WebKit来实现的,但JavaScript引擎使用的是V8。由于是两个相互独立的功能,两者之间通过接口连接,因此在JavaScript中访问DOM就会产生消耗,天生就慢。
重绘与重排
浏览器中存在两个数据接口:
页面结构
表示DOM树的显示
一旦DOM和渲染树构建完成,浏览器就开始绘制。当DOM的变化影响了元素的几何属性,浏览器需要重新计算元素的几何属性,同时其他元素的几何属性和位置也会受到影响。这是浏览器会发生“重排”,重新构建渲染树,重排之后,浏览器将重新绘制受到影响的部分到屏幕上,称为“重绘”。
不是所有的DOM变化都会导致重排,例如改变元素的背景色,这种情况下只会发生一次重绘。
导致重排的因素:
- 元素的显隐
- 元素位置的改变
- 元素尺寸的改变(内外边距,边框宽度,高度宽度)
- 元素内容改变
- 页面初始化
- 浏览器窗口尺寸的改变
优化重排和重绘
合并样式修改
较慢
1 2 3
| var el = document.getElementById('div') el.style.borderLeft = '1px' el.style.borderRight = '2px'
|
优化
1 2
| var el = document.getElementById('div') el.style.cssText += ';border-left: 1px; border-right: 2px'
|
或者通过改变class
1 2
| var el = document.getElementById('div') el.className = 'change'
|
批量修改DOM
批量修改DOM的通用步骤:
- 元素脱离文档
- 进行修改
- 插入文档
例如存在如下DOM结构
1 2 3 4
| <ul> <li>aaa</li> <li>bbb</li> </ul>
|
现要根据数据渲染li结构,以下是修改li节点的函数
1 2 3 4 5 6 7 8 9
| function renderList(parentNode, data) { var li for (var i = 0; i < data.length; i++) { li = document.createElement('li') ... parentNode.appendChild(li) } }
|
较慢
1 2 3
| var data = [1, 2, 3] var ul = document.getElementById('ul') renderList(ul, data)
|
使用上述方法,每次插入一个li都会导致重排,我们可以按之前的通用方式进行优化
1 2 3 4 5
| var data = [1, 2, 3] var ul = document.getElementById('ul') ul.style.display = 'none' renderList(ul, data) ul.style.display = 'block'
|
或者通过创建代码片段进行优化
1 2 3 4 5
| var data = [1, 2, 3] var ul = document.getElementById('ul') var fragment = document.createDocumentFragment() renderList(fragment, data) ul.appendChild(fragment)
|
元素脱离动画流
事件委托
基于事件的冒泡机制,在父级节点绑定处理器,处理子元素的事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var parent = document.getElementById('parent') parent.onclick = function(evt) { evt = evt || window.event var target = evt.target || evt.srcElement ... if (typeof evt.preventDefault === 'function') { evt.preventDefault() evt.stopPropagetion() } else { evt.returnValue = false evt.cancelBubble = false } }
|