[转]DOMContentLoaded与load的区别

本文转自 https://www.cnblogs.com/caizhenbo/p/6679478.html

(1)在chrome浏览器的开发过程中,我们会看到network面板中有这两个数值,分别对应网 络请求上的标志线,这两个时间数值分别代表什么?

(2)我们一再强调将css放在头部,将js文件放在尾部,这样有利于优化页面的性能,为什么这种方式能够优化性能?

(3)在用jquery的时候,我们一般都会将函数调用写在ready方法内,这是什么原理?

首先看一下

DOMContentLoaded顾名思义,就是dom内容加载完毕。那什么是dom内容加载完毕呢?我们从打开一个网页说起。当输入一个URL,页面的展示首先是空白的,然后过一会,页面会展示出内容,但是页面的有些资源比如说图片资源还无法看到,此时页面是可以正常的交互,过一段时间后,图片才完成显示在页面。从页面空白到展示出页面内容,会触发DOMContentLoaded事件。而这段时间就是HTML文档被加载和解析完成。

这时候问题又来了,什么是HTML文档被加载和解析完成。要解决这个问题,我们就必须了解浏览器渲染原理。

当我们在浏览器地址输入URL时,浏览器会发送请求到服务器,服务器将请求的HTML文档发送回浏览器,浏览器将文档下载下来后,便开始从上到下解析,解析完成之后,会生成DOM。如果页面中有css,会根据css的内容形成CSSOM,然后DOM和CSSOM会生成一个渲染树,最后浏览器会根据渲染树的内容计算出各个节点在页面中的确切大小和位置,并将其绘制在浏览器上。

下面就是页面加载和解析过程中,浏览器的一个快照

上面我们看到在解析html的过程中,html的解析会被中断,这是因为javascript会阻塞dom的解析。当解析过程中遇到<script>标签的时候,便会停止解析过程,转而去处理脚本,如果脚本是内联的,浏览器会先去执行这段内联的脚本,如果是外链的,那么先会去加载脚本,然后执行。在处理完脚本之后,浏览器便继续解析HTML文档。

同时javascript的执行会受到标签前面样式文件的影响。如果在标签前面有样式文件,需要样式文件加载并解析完毕后才执行脚本。这是因为javascript可以查询对象的样式。

这里需要注意一点,在现在浏览器中,为了减缓渲染被阻塞的情况,现代的浏览器都使用了猜测预加载。当解析被阻塞的时候,浏览器会有一个轻量级的HTML(或CSS)扫描器(scanner)继续在文档中扫描,查找那些将来可能能够用到的资源文件的url,在渲染器使用它们之前将其下载下来。

在这里我们可以明确DOMContentLoaded所计算的时间,当文档中没有脚本时,浏览器解析完文档便能触发 DOMContentLoaded 事件;如果文档中包含脚本,则脚本会阻塞文档的解析,而脚本需要等位于脚本前面的css加载完才能执行。在任何情况下,DOMContentLoaded 的触发不需要等待图片等其他资源加载完成。

接下来,我们来说说load,页面上所有的资源(图片,音频,视频等)被加载以后才会触发load事件,简单来说,页面的load事件会在DOMContentLoaded被触发之后才触发。

我们在 jQuery 中经常使用的 $(document).ready(function() { // ...代码... }); 其实监听的就是 DOMContentLoaded 事件,而 $(document).load(function() { // ...代码... }); 监听的是 load 事件。在用jquery的时候,我们一般都会将函数调用写在ready方法内,就是页面被解析后,我们就可以访问整个页面的所有dom元素,可以缩短页面的可交互时间,提高整个页面的体验。

下面我们在来看看如何实现这两个函数

1、onload事件

onload事件所有的浏览器都支持,所以我们不需要什么兼容,只要通过调用

window.onload = function(){

}

2、DOMContentLoaded 事件

DOMContentLoaded不同的浏览器对其支持不同,所以在实现的时候我们需要做不同浏览器的兼容。

1)支持DOMContentLoaded事件的,就使用DOMContentLoaded事件;

2)IE6、IE7不支持DOMContentLoaded,但它支持onreadystatechange事件,该事件的目的是提供与文档或元素的加载状态有关的信息。

3)  更低的ie还有个特有的方法doScroll, 通过间隔调用:document.documentElement.doScroll("left");

可以检测DOM是否加载完成。 当页面未加载完成时,该方法会报错,直到doScroll不再报错时,就代表DOM加载完成了。该方法更接近DOMContentLoaded的实现。

function ready(fn){

    if(document.addEventListener) {
        document.addEventListener(‘DOMContentLoaded‘, function() {
            document.removeEventListener(‘DOMContentLoaded‘,arguments.callee, false);
            fn();
        }, false);
    } 

    // 如果IE
    else if(document.attachEvent) {
        // 确保当页面是在iframe中加载时,事件依旧会被安全触发
        document.attachEvent(‘onreadystatechange‘, function() {
            if(document.readyState == ‘complete‘) {
                document.detachEvent(‘onreadystatechange‘, arguments.callee);
                fn();
            }
        });

        // 如果是IE且页面不在iframe中时,轮询调用doScroll 方法检测DOM是否加载完毕
        if(document.documentElement.doScroll && typeof window.frameElement === "undefined") {
            try{
                document.documentElement.doScroll(‘left‘);
            }
            catch(error){
                return setTimeout(arguments.callee, 20);
            };
            fn();
        }
    }
};

 最后我们来回答这个问题:我们为什么一再强调将css放在头部,将js文件放在尾部

在面试的过程中,经常会有人在回答页面的优化中提到将js放到body标签底部,原因是因为浏览器生成Dom树的时候是一行一行读HTML代码的,script标签放在最后面就不会影响前面的页面的渲染。那么问题来了,既然Dom树完全生成好后页面才能渲染出来,浏览器又必须读完全部HTML才能生成完整的Dom树,script标签不放在body底部是不是也一样,因为dom树的生成需要整个文档解析完毕。

我们再来看一下chrome在页面渲染过程中的,绿色标志线是First Paint的时间。纳尼,为什么会出现firstpaint,页面的paint不是在渲染树生成之后吗?其实现代浏览器为了更好的用户体验,渲染引擎将尝试尽快在屏幕上显示的内容。它不会等到所有HTML解析之前开始构建和布局渲染树。部分的内容将被解析并显示。也就是说浏览器能够渲染不完整的dom树和cssom,尽快的减少白屏的时间。假如我们将js放在header,js将阻塞解析dom,dom的内容会影响到First Paint,导致First Paint延后。所以说我们会将js放在后面,以减少First Paint的时间,但是不会减少DOMContentLoaded被触发的时间。

原文地址:https://www.cnblogs.com/leon-2016/p/9442315.html

时间: 2024-11-01 01:32:25

[转]DOMContentLoaded与load的区别的相关文章

DOMContentLoaded与load的区别

(1)在chrome浏览器的开发过程中,我们会看到network面板中有这两个数值,分别对应网 络请求上的标志线,这两个时间数值分别代表什么? (2)我们一再强调将css放在头部,将js文件放在尾部,这样有利于优化页面的性能,为什么这种方式能够优化性能? (3)在用jquery的时候,我们一般都会将函数调用写在ready方法内,这是什么原理? 首先看一下 DOMContentLoaded顾名思义,就是dom内容加载完毕.那什么是dom内容加载完毕呢?我们从打开一个网页说起.当输入一个URL,页面

事件DOMContentLoaded和load的区别

他们的区别是,触发的时机不一样,先触发DOMContentLoaded事件,后触发load事件. DOM文档加载的步骤为 解析HTML结构. 加载外部脚本和样式表文件. 解析并执行脚本代码. DOM树构建完成.//DOMContentLoaded 加载图片等外部文件. 页面加载完毕.//load 在第4步,会触发DOMContentLoaded事件.在第6步,触发load事件.

页面加载事件DOMContentLoaded和load(相关文章)

1:事件DOMContentLoaded和load的区别 http://www.jianshu.com/p/d851...2:JS异步加载的三种方式 http://blog.csdn.net/l5227032... (总结:生活中,我们很多事情要搭建主框架,然后再在主框架的指引下做具体的内容:html页面也是如此,先加载主体的框架也就是dom结构,然后再在里面添加:图片,flash等:这就分别对应两种对应的事件即:DOMContentLoaded和load事件,jquery对应的就是ready和

ready与load的区别

JQuery里有ready和load事件 $(document).ready(function() { // ...代码... }) //document ready 简写 $(function() { // ...代码... }) $(document).load(function() { // ...代码... }) 他们的主要区别为ready先执行,load后执行. DOM文档加载的步骤: (1) 解析HTML结构. (2) 加载外部脚本和样式表文件. (3) 解析并执行脚本代码. (4)

DOMContentLoaded vs load

DOMContentLoaded The DOMContentLoaded event is fired when the document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading (the load event can be used to detect a fully-loaded page). 具体意思就是:

Hibernate之查询中get()和load()的区别,list()和iterate()的区别

[Hibernate]之查询中get()和load()的区别,list()和iterate()的区别 list()查询 //一次性把数据对象取出来 @Test public void findTestList(){ Session s=sessionFactory.getCurrentSession(); s.beginTransaction(); List<Person> persons=s.createQuery("from Person").list(); for(P

Hibernate的Session的get()和load()方法区别

hibernate中Session接口提供的get()和load()方法都是用来获取一个实体对象,在使用方式和查询性能上有一些区别. get Session接口提供了4个重载的get方法,分别通过"持久类+主键"和"全类名+主键"以及"锁选项"来获取实体对象. public Object get(Class clazz, Serializable id); public Object get(Class clazz, Serializable i

jQuery document window load ready 区别详解

用过JavaScript的童鞋,应该知道window对象和document对象,也应该听说过load事件和ready事件,小菜当然也知道,而且自认为很了解,直到最近出了问题,才知道事情并不是那么简单. 首先说说window和document,直观上来讲,window代表的是浏览器窗口,而document代表的是浏览器窗口中加载的dom元素,进一步说,document是window的一个属性,window是最顶级的对象. 二者有啥区别呢?很好理解,假设现在有一个浏览器,里边加载的页面特别长,已经超

Hibernate中get()和load()的区别

Hibernate中根据Id单条查询获取对象的方式有两种,分别是get()和load(),来看一下这两种方式的区别. 1. get() 使用get()来根据ID进行单条查询: User user=session.get(User.class, "1"); 当get()方法被调用的时候就会立即发出SQL语句: Hibernate: select user0_.ID as ID1_1_0_, user0_.CREATETIME as CREATETI2_1_0_, user0_.UPDAT