html5中文学习网

您的位置: 首页 > 网站及特效实例 > javascript特效 » 正文

JS、CSS以及img对DOMContentLoaded事件的影响_javascript技巧_

[ ] 已经帮助:人解决问题

前端的纯技术就是对规范的认知WJTHTML5中文学习网 - HTML5先行者学习网

什么是DOMContentLoaded事件?WJTHTML5中文学习网 - HTML5先行者学习网

首先想到的是查看W3C的HTML5规范,DOMContentLoaded事件在什么时候触发:WJTHTML5中文学习网 - HTML5先行者学习网

Once the user agent stops parsing the document, the user agent must run the following steps:WJTHTML5中文学习网 - HTML5先行者学习网
1. Set the current document readiness to “interactive” and the insertion point to undefined.WJTHTML5中文学习网 - HTML5先行者学习网
Pop all the nodes off the stack of open elements.WJTHTML5中文学习网 - HTML5先行者学习网
2. If the list of scripts that will execute when the document has finished parsing is not empty, run these substeps:WJTHTML5中文学习网 - HTML5先行者学习网
2.1 Spin the event loop until the first script in the list of scripts that will execute when the document has finished parsing has its “ready to be parser-executed” flag set and the parser's Document has no style sheet that is blocking scripts.WJTHTML5中文学习网 - HTML5先行者学习网
2.2 Execute the first script in the list of scripts that will execute when the document has finished parsing.WJTHTML5中文学习网 - HTML5先行者学习网
2.3 Remove the first script element from the list of scripts that will execute when the document has finished parsing (i.e. shift out the first entry in the list).WJTHTML5中文学习网 - HTML5先行者学习网
2.4 If the list of scripts that will execute when the document has finished parsing is still not empty, repeat these substeps again from substep 1.WJTHTML5中文学习网 - HTML5先行者学习网
3. Queue a task to fire a simple event that bubbles named DOMContentLoaded at the Document.WJTHTML5中文学习网 - HTML5先行者学习网

规范总是那么的晦涩,但至少有一点是可以明确了的,就是在JS(不包括动态插入的JS)执行完之后,才会触发DOMContentLoaded事件。WJTHTML5中文学习网 - HTML5先行者学习网

接下来看看MDN上有关DOMContentLoaded事件的文档WJTHTML5中文学习网 - HTML5先行者学习网

The DOMContentLoaded event is fired when the document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loadingWJTHTML5中文学习网 - HTML5先行者学习网
Note: Stylesheet loads block script execution, so if you have a <script> after a <link rel="stylesheet" ...>, the page will not finish parsing and DOMContentLoaded will not fire until the stylesheet is loaded.WJTHTML5中文学习网 - HTML5先行者学习网

这么看来,至少可以得出这么一个理论:DOMContentLoaded事件本身不会等待CSS文件、图片、iframe加载完成。WJTHTML5中文学习网 - HTML5先行者学习网
它的触发时机是:加载完页面,解析完所有标签(不包括执行CSS和JS),并如规范中所说的设置 interactive 和执行每个静态的script标签中的JS,然后触发。WJTHTML5中文学习网 - HTML5先行者学习网
而JS的执行,需要等待位于它前面的CSS加载(如果是外联的话)、执行完成,因为JS可能会依赖位于它前面的CSS计算出来的样式。WJTHTML5中文学习网 - HTML5先行者学习网

实践是检验真理的唯一标准WJTHTML5中文学习网 - HTML5先行者学习网

实验1:DOMContentLoaded事件不直接等待CSS文件、图片的加载完成WJTHTML5中文学习网 - HTML5先行者学习网

index.html:WJTHTML5中文学习网 - HTML5先行者学习网

<!DOCTYPE html><html lang="zh-CN"><head>  <meta charset="UTF-8">  <title></title>  <link rel="stylesheet" type="text/css" href="./css/main.css" rel="external nofollow" rel="external nofollow" ></head><body>  <p>Content</p>  <img src="./img/chrome-girl.jpg"></body></html>

71fca778-a249-11e3-8824-2aae4440c857WJTHTML5中文学习网 - HTML5先行者学习网
WJTHTML5中文学习网 - HTML5先行者学习网

图一WJTHTML5中文学习网 - HTML5先行者学习网

如果页面中没有script标签,DOMContentLoaded事件并没有等待CSS文件、图片加载完成。WJTHTML5中文学习网 - HTML5先行者学习网

Chrome开发者工具的Timeline面板可以帮我们记录下浏览器的一举一动。图一中红色小方框中的蓝线,表示DOMContentLoaded事件,它右边的红线和绿线分别表示load事件和First paint,鼠标hover在这些线露出灰色方框下面的一小部分时就会出现带有说明文字的tips(这交互够反人类的对吧!)。WJTHTML5中文学习网 - HTML5先行者学习网

实验2:DOMContentLoaded事件需要等待JS执行完才触发WJTHTML5中文学习网 - HTML5先行者学习网

index.html:WJTHTML5中文学习网 - HTML5先行者学习网

<!DOCTYPE html><html lang="zh-CN"><head>  <meta charset="UTF-8">  <title></title>  <script type="text/javascript">    console.timeStamp('Inline script before link in head');    window.addEventListener('DOMContentLoaded', function(){      console.timeStamp('DOMContentLoaded event');    });  </script>  <link rel="stylesheet" type="text/css" href="./css/main.css" rel="external nofollow" rel="external nofollow" >  <script type="text/javascript">    console.timeStamp('Inline script after link in head');  </script></head><body>  <p>Content</p>  <img src="./img/chrome-girl.jpg">  <script type="text/javascript" src="./js/main.js"></script></body></html>

main.js:WJTHTML5中文学习网 - HTML5先行者学习网
console.timeStamp('External script after link in body');WJTHTML5中文学习网 - HTML5先行者学习网
WJTHTML5中文学习网 - HTML5先行者学习网

dcf399e8-a252-11e3-92c1-c3dbad820909WJTHTML5中文学习网 - HTML5先行者学习网

图二WJTHTML5中文学习网 - HTML5先行者学习网

如果页面中静态的写有script标签,DOMContentLoaded事件需要等待JS执行完才触发。WJTHTML5中文学习网 - HTML5先行者学习网

而script标签中的JS需要等待位于其前面的CSS的加载完成。WJTHTML5中文学习网 - HTML5先行者学习网

console.timeStamp() 可以向Timeline中添加一条记录,并对应上方的一条黄线。WJTHTML5中文学习网 - HTML5先行者学习网

从图二中可以看出,在CSS之前的JS立刻得到了执行,而在CSS之后的JS,需要等待CSS加载完后才执行,比较明显的是main.js早就加载完了,但还是要等main.css加载完才能执行。而DOMContentLoaded事件,则是在JS执行完后才触发。滑动Timeline面板中表示展示区域的滑块,如图三,放大后即可看到表示DOMContentLoaded事件的蓝线(之前跟黄线和绿线靠的太近了),当然,通过 console.timeStamp() 向TimeLine中添加的记录也可证明其触发时间。WJTHTML5中文学习网 - HTML5先行者学习网

910b5c2c-a253-11e3-995d-e19fb254cf4eWJTHTML5中文学习网 - HTML5先行者学习网
WJTHTML5中文学习网 - HTML5先行者学习网

图三WJTHTML5中文学习网 - HTML5先行者学习网

现代浏览器会并发的预加载CSS, JS,也就是一开始就并发的请求这些资源,但是,执行CSS和JS的顺序还是按原来的依赖顺序(JS的执行要等待位于其前面的CSS和JS加载、执行完)。先加载完成的资源,如果其依赖还没加载、执行完,就只能等着。WJTHTML5中文学习网 - HTML5先行者学习网

实验3:img何时开始解码、绘制?WJTHTML5中文学习网 - HTML5先行者学习网

WJTHTML5中文学习网 - HTML5先行者学习网
从图三中我们可以发现一个有趣的地方:img的请求老早就发出了,但延迟了一段时间才开始解码。如图二、图三中的红框所示,截图中只框出了一部分表示解码的记录,而实际上这些表示解码的记录一直持续到img加载结束,如图四所示,img是一边加载一边解码的:WJTHTML5中文学习网 - HTML5先行者学习网

7384a57a-a256-11e3-9c4a-b857956eaeedWJTHTML5中文学习网 - HTML5先行者学习网
WJTHTML5中文学习网 - HTML5先行者学习网

图三WJTHTML5中文学习网 - HTML5先行者学习网

现代浏览器会并发的预加载CSS, JS,也就是一开始就并发的请求这些资源,但是,执行CSS和JS的顺序还是按原来的依赖顺序(JS的执行要等待位于其前面的CSS和JS加载、执行完)。先加载完成的资源,如果其依赖还没加载、执行完,就只能等着。WJTHTML5中文学习网 - HTML5先行者学习网

实验3:img何时开始解码、绘制?WJTHTML5中文学习网 - HTML5先行者学习网

从图三中我们可以发现一个有趣的地方:img的请求老早就发出了,但延迟了一段时间才开始解码。如图二、图三中的红框所示,截图中只框出了一部分表示解码的记录,而实际上这些表示解码的记录一直持续到img加载结束,如图四所示,img是一边加载一边解码的:WJTHTML5中文学习网 - HTML5先行者学习网

7384a57a-a256-11e3-9c4a-b857956eaeedWJTHTML5中文学习网 - HTML5先行者学习网
图四WJTHTML5中文学习网 - HTML5先行者学习网

抱着“猜想――验证”的想法,我猜想这是因为img这个资源是否需要展现出来,需要等 所有的JS和CSS的执行完 才知道,因为main.js可能会执行某些DOM操作,比如删除这个img元素,或者修改其src属性,而CSS可能会将其 display: noneWJTHTML5中文学习网 - HTML5先行者学习网

imageWJTHTML5中文学习网 - HTML5先行者学习网
图五WJTHTML5中文学习网 - HTML5先行者学习网

imageWJTHTML5中文学习网 - HTML5先行者学习网
图六WJTHTML5中文学习网 - HTML5先行者学习网

imageWJTHTML5中文学习网 - HTML5先行者学习网
图七WJTHTML5中文学习网 - HTML5先行者学习网

图五中没有JS和CSS,img的数据一接收到就马上开始解码了。WJTHTML5中文学习网 - HTML5先行者学习网
图六中没有JS,但img要等到CSS加载完才开始解码。WJTHTML5中文学习网 - HTML5先行者学习网
图七的代码跟图六的代码唯一的区别是CSS把img给 display: none; ,这使得img虽然请求了,但根本没有进行解码。WJTHTML5中文学习网 - HTML5先行者学习网
这说明,img是否需要解码、绘图(paint)出来,确实需要等CSS加载、执行完才能知道。也就是说,CSS会阻塞img的展现!那么JS呢?WJTHTML5中文学习网 - HTML5先行者学习网

imageWJTHTML5中文学习网 - HTML5先行者学习网
图八WJTHTML5中文学习网 - HTML5先行者学习网

图八对应的代码:WJTHTML5中文学习网 - HTML5先行者学习网

<!DOCTYPE html><html lang="zh-CN"><head>  <meta charset="UTF-8">  <title></title>  <script type="text/javascript">    console.timeStamp('Inline script in head');    window.addEventListener('DOMContentLoaded', function(){      console.timeStamp('DOMContentLoaded event');    });  </script></head><body>  <p>Content</p>  <img src="./img/chrome-girl.jpg">  <script type="text/javascript" src="./js/main.js"></script></body></html>

非常令人惊讶,在有JS而没有CSS的页面中,img居然能够在收到数据后就立刻开始解码、绘图(paint),也就是说,JS并没有阻塞img的展现!这跟我们以前理解的JS会阻塞img资源的传统观念不太一样,看来Chrome对img的加载和展现做了新的优化。WJTHTML5中文学习网 - HTML5先行者学习网

我们常用的jQuery的 $(document).ready() 方法,就是对DOMContentLoaded事件的监听(当然,其内部还会通过模拟DOMContentLoaded事件和监听onload事件来提供降级方案)。通常推荐在DOMContentLoaded事件触发的时候为DOM元素注册事件。所以尽快的让DOMContentLoaded事件触发,就意味着能够尽快让页面可交互:WJTHTML5中文学习网 - HTML5先行者学习网

减小CSS文件体积,把单个CSS文件分成几个文件以并行加载,减少CSS对JS的阻塞时间WJTHTML5中文学习网 - HTML5先行者学习网

次要的JS文件,通过动态插入script标签来加载(动态插入的script标签不阻塞DOMContentLoaded事件的触发)WJTHTML5中文学习网 - HTML5先行者学习网

CSS中使用的精灵图,可以利用对img的预加载,放在html中跟CSS文件一起加载WJTHTML5中文学习网 - HTML5先行者学习网

在做实验的过程中,感觉Chrome开发者工具的Timeline面板非常强大,浏览器的一举一动都记录下来。以前我们前端开发要想理解、探索浏览器的内部行为,或者摸着石头过河的做黑盒测试,或者事倍功半的研究浏览器源码,唯一高效点的做法就是学习别人的研究经验,看老外的文章,但浏览器的发展日新月异(比如这次实验发现的JS不阻塞img的展现),别人的经验始终不是最新、最适合的,关键是要结合自己的业务、需求场景,有针对性的做分析和优化。WJTHTML5中文学习网 - HTML5先行者学习网

PS.WJTHTML5中文学习网 - HTML5先行者学习网
以上测试环境为windows/chrome,并用Fiddler模拟慢速网络WJTHTML5中文学习网 - HTML5先行者学习网

(责任编辑:)
推荐书籍
推荐资讯
关于HTML5先行者 - 联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明 - 人才招聘 - 帮助