DomReady
HTML标签和DOM
我们经常看人们用:
document.getElementById('xxx').style/left = "80px"
结果却报错说找不到元素,但是页面上明明包含id为xxx的这个元素,这实际上就是没分清HTML标签于DOM节点。HTML是一种标记语言,它告诉我们页面上有什么内容。但行为交互是需要通过DOM操作实现的,不要以为那两个尖括号的内容就是一个DOM。HTML标签要经过浏览器解析才会变成DOM节点。当我们向地址栏传入一个URL,开始加载页面,到我们看到内容,这期间就有一个DOM节点构建的过程。节点们是以树的形式组织的,当页面上所有HTML都转为节点,这就叫做DOM树构建完成,简称之DomReady
。
HTML转为为DOM是一个复杂的过程,可以参考这个:HTML TO DOM.
我们简单说一下,浏览器是从上到下,从左到右,一个个字符串读入,大致可以认为两个同名的开标签与闭标签就是一个DOM(有的是没有闭签),这时就忽略掉它的两个标签间的内容。页面上有许多标签,但标签会生成同样多的DOM,因为有的标签下只允许存在特定的子标签,比如tr
下面一定是td
,th
, select
下面一定是opgroup
,option
,而option
下面,就算你写了<span></span>
,它都会忽略掉,option
下面只存在文本,这就是我们需要自定义下拉框的缘故。我们说过,这顺序是从上到下,有的元素很简单,会构建得很快。但标签存在src,href属性,它会引用外部资源,这就要区别对待了。比如说,script标签,它一定会等src指定的脚本文件加载下来,然后全部执行了里面的脚本,才会分析下一个标签。这种现象叫做堵塞。堵塞是一种非常致命的现象,因为浏览器渲染引擎是单线程的,如果头部脚本过多过大会导致白屏,影响用户体验,因此雅虎的20军规就有一条提到,将所有script标签放到body之后。此外,style标签与link标签,它们在加载样式文件时是不会堵塞,但它们一旦异步加载好,就立即开始渲染已经构建好的元素节点们,这可能会引起重绘,这也影响速度。另一个影响DOM树构建的因此是iframe,它也会加载资源,虽然不会堵塞DOM构建,但它由于是发出HTTP请求,而HTTP请求是有限,它会与父标签的其他需要加载外部资源的标签产生竞争。我们经常看到一些新闻网,上面会挂许多iframe广告,这些页面一开始加载时就很卡,也是这缘故。此外还有object元素,用来加载flash等等,这些东西都会影响到DOM树的构建过程。因此在这时候,当我们贸贸然,使用getElementById
,getElementsByTagName
获取元素,然后操作它们,就会有很大机率碰到元素为null的异常。这时,目标元素还可以没有转换为DOM节点,还只是一个普通的字符串呢!
我们又不能随意写一个
setTimeout(function(){
document.getElementById("xxx").style.left = "80px"
}, 3000)
这完全是靠蒙,可能有效,也可能失败。因此获得所有标签都转换为DOM节点的时机就非常重要。很早期,浏览器提供了一个window.onload
方法,但这东西是等到所有标签变成DOM,并且外部资源、图片、背景音乐什么都加载好才触发,时间上有点晚。幸好,浏览器提供了一个document.readyState
属性,当它变成complete时,说明这时机到了。但这是一个属性,不是一个事件,需要使用不太精确的setInterval轮询。现在,W3C终于绅士地提供了一个DOMContentLoaded
事件。
DOMContentLoaded
DOMContentLoaded
应该是最好用的,它是一个事件,代表着虽然stylesheet,images这些资源没有加载好,但HTMLDocument完全被加载并解析好了,已经可以安全地操作DOM了。
document.addEventListener("DOMContentLoaded", function(event) {
console.log("DOM fully loaded and parsed");
});
load
load触发的时机是所有的资源全部加载完成,这个时候才操作DOM实际上有点晚了。
document.addEventListener("load", function(event) {
console.log("All resources finished loading!");
});
readystatechange
// alternative to DOMContentLoaded
document.onreadystatechange = function () {
if (document.readyState == "interactive") {
console.log('DOMContentLoaded')
}
}
//alternative to window.onload
document.onreadystatechange = function(){
if(document.readyState == "completed"){
console.log('load')
}
}
修改自司徒正美的博文,原链接
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。