DOM实践 💫

DOM(Document Object Model),文档对象模型

document.readyState 和 readystatechange

因为有了DOMContendLoadedload事件更能在页面DOM加载完成和页面所有资源加载完成的时候被准确的触发。所以,readystatechange事件已经不太常用。

最近在看sea.js的源码,发现这个事件在IE6-8浏览器上可以作为以上两个事件的替代品,因为在IE6-8,以上两个事件有些时候并不能按照我们的预期执行。
并且,在所有浏览器中,document上是都有readystatechangereadyState属性的,但是在具体element上,只有IE上有这个事件和属性。

sea.js中,需要在页面中插入一个script标签,在script远程加载并执行后,及时的触发scriptload事件。因为可能同时插入多个script标签,要保证每个脚本的插入顺序和load事件的触发顺序一致,但在IE6-8中,却并不能保证。

但是,好巧不巧。在IE6-8中,如果当前script中的代码正在执行,那么这个scriptreadyState属性值会变成interactive,因为浏览器只会单线程执行代码,所以当前执行的script只会有一个。从而可以模拟load事件,达到目的。

sea.js之所以要保证顺序,是因为它会把每个script(模块)的信息保存在一个临时变量中,在script代码执行完成后,设置scriptsrc属性为这个
模块的标识符,然后置空这个变量,移除这个script,继续下一个。因为只有这个一个临时变量,所以必须保证执行顺序和事件触发顺序的同步。

document.createDocumentFragment

如果你要向DOM中插入很多个子节点。可以先把这些子节点插入到一个空白节点中,最后将这个空白节点插入到DOM中,这样可以减少页面reflow,节约性能。

批量插入DOM

1
2
3
4
5
6
7
8
9
10
11
12
var arr = []
var len = 5
for(var i = 0; i < len; i++) {
var el = document.createElement(div)
el.textContent = i
arr.push(el)
}

// 会引起5次 reflow
for(var j = 0; j < len; j++) {
document.body.appendChild(arr[j])
}

使用document.createDocumentFragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var arr = []
var len = 5
for(var i = 0; i < len; i++) {
var el = document.createElement(div)
el.textContent = i
arr.push(el)
}

var blank = document.createDocumentFragment()

for(var j = 0; j < len; j++) {
blank.appendChild(arr[j])
}

// 只会引起一次reflow
document.body.appendChild(blank)

这个原理跟使用「JS操作CSS尽量操作类名而不是具体style」的方法一样,只不过后者减少的是页面的repaint

直接操作单个属性

1
2
3
4
5
6
var el = document.getElementById('app')

// 引起3次 repaient
el.style.color = '#000'
el.style.fontSize = '18px'
el.style.fontWeight = 'bold'

操作类名

1
2
3
4
var el = document.getElementById('app')

// 引起一次 repaient
el.className += ' test'

1
2
3
4
5
.test {
color: #000;
font-size: 18px;
font-weight: bold;
}

document.write

参考

  1. document.readyState
  2. document.createDocumentFragment
  3. 闲扯 『 document.write 』