HTML解析之DOMContentLoaded和onload

HTML解析之DOMContentLoaded和onload说在前面在很久很久以前,我在封装自己的JQuery库时就使用过DOMContentLoaded,觉得这个知识点看看别的文章就行了,不过现在我想把它记下来。JS代码与body标签的位置关系一个HTML初学时会遇到的问题,一个html页面中js代码应该放到哪里?<!–如果script标签在body前面–><head>…&lt…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

说在前面

在很久很久以前,我在封装自己的JQuery库时就使用过DOMContentLoaded,觉得这个知识点看看别的文章就行了,不过现在我想把它记下来。

 

JS代码与body标签的位置关系

一个HTML初学时会遇到的问题,一个html页面中js代码应该放到哪里?

<!--如果script标签在body前面-->
<head>
    ...
    <script>
        var box = document.getElementById('box');
        console.log(box)  //null
    </script>
</head>
<body>
    <div id="box"></div>
</body>
---------
<!--如果script标签在body后面(里面的最后也可以)-->
<head></head>
<body>
    <div id="box"></div>
    <script>
        var box = document.getElementById('box');
        console.log(box)  //div#box
    </script>
</body>

Jetbrains全家桶1年46,售后保障稳定

上面代码可以看出,如果js代码写在body标签前面,而且没用其他事件而直接获取dom的话是无法获取的。而js代码写在body标签的后面(里面的最后也可以)就可以获取dom。

实际上如果了解浏览器解析HTML规则就很清楚原因了,浏览器解析HTML由上往下依次执行,如果遇到<script>会阻塞解析,先执行该JS脚本(如果是外部JS文件还要先加载),执行结束后再接着往下解析,所以上面获取不到dom的原因是当时JS代码执行时页面DOM树尚未构建完成。具体分析往下看。

 

script标签的defer和async

从上面知道,浏览器解析HTML遇到script标签会阻塞。上面举例的JS代码都是内嵌在HTML中的,这样再解析到script时直接执行就行。但如果是引入外部JS文件的话会有一点不同,要先加载该JS文件,然后执行,然后在往下解析HTML。但script标签上还有两个常见属性defer和async

一般情况<script src=”xxx.js”>

当浏览器遇到 script 标签时,文档的解析将停止,并立即下载并执行脚本,脚本执行完毕后将继续解析文档。

defer <script defer src=”xxx.js”>

当浏览器遇到 script 标签时,文档的解析不会停止,JS文件的加载与文档解析并行(异步),待到文档解析DOM构建完成,脚本才会执行(在DOMContentLoaded事件触发之前)。

async <script async src=”xxx.js”>

当浏览器遇到 script 标签时,文档的解析不会停止,JS文件的加载与文档解析并行(异步),脚本下载完成后开始执行脚本,脚本执行时文档会停止解析。

看图(图片来源于网络)

HTML解析之DOMContentLoaded和onload

 

蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

总结defer和async的区别:

  1. 加载时是一样的,相对于HTML解析是异步的。
  2. 不同的是执行时机,async在代码加载完之后会马上执行,并且执行时会阻塞HTML解析。而defer则要等到文档解析DOM构建完成,DOMContentLoaded事件触发之前执行
  3. async执行时机不确定性,要注意使用场景。

所以script标签加上defer属性,即使不用DOMContentLoaded或window.onload也可以获取操作DOM。

//index.js
var box = document.getElementById('box');
//可以获取到div
console.log(box);  //div#box
--------
<head>
    ...
    <script defer src="./index.js"></script>
</head>
<body>
    <div id="box"></div>
</body>

 

DOMContentLoaded和window.onload

DomContentLoaded

MDN解释当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。另一个不同的事件 load 应该仅用于检测一个完全加载的页面。 在使用 DOMContentLoaded 更加合适的情况下使用 load 是一个令人难以置信的流行的错误,所以要谨慎。

注意:DOMContentLoaded 事件必须等待其所属script之前的样式表加载解析完成才会触发

//index.css
#box{
    width:100px;
    height:100px;
    background: pink;
}
---------
//DOMContentLoaded前面引入样式表
<head>
    <link rel="stylesheet" href="./index.css"/>
    <script>
        function getStyle(el, attr) {
            return el.currentStyle ? el.currentStyle[attr] : getComputedStyle(el)[attr];
        }
        // DOMContentLoaded
        document.addEventListener('DOMContentLoaded', function(){
            var box = document.getElementById('box');
            //可以获取到样式表的样式
            console.log(getStyle(box, 'width'));    //100px
        })
    </script>
</head>
<body>
    <div id="box"></div>
</body>
--------
//DOMContentLoaded后面引入样式表
<head>
    <script>
        function getStyle(el, attr) {
            return el.currentStyle ? el.currentStyle[attr] : getComputedStyle(el)[attr];
        }
        // DOMContentLoaded
        document.addEventListener('DOMContentLoaded', function(){
            var box = document.getElementById('box');
            //可能无法获取到样式表的样式
            console.log(getStyle(box, 'width'));    //1350px或100px
        })
    </script>
    <link rel="stylesheet" href="./index.css"/>
</head>
<body>
    <div id="box"></div>
</body>

 

上面代码看出,在DOMContentLoaded后面引入样式表,DOMContentLoaded可能无法获取样式表里的样式,因为此时HTML解析完成,DOM树构建完成,但外部css文件可能还没加载完成

暂时得出结论:js代码应该放在样式表之后

window.onload

这个就没什么好说的,此时HTML文档解析完成,其他依赖资源也全部加载完成。

用document.readyState看一下各种情况下的HTML文档状态:

<script>
    window.onload = function(){
        console.log('window.onload', document.readyState);
    }
    document.addEventListener('DOMContentLoaded', function(){
        console.log('DOMCOntentLoaded', document.readyState);
    }, false);
    console.log(document.readyState);
</script>
//输出
//loading    文档加载中
//DOMCOntentLoaded interactive    文档与用户可以开始交互,可以操作DOM
//window.onload complete    一切完成

 

ready()简单实现

JQuery中有个$(document).ready(),下面就简单实现一下。

document.ready = function(fn){
    if(document.addEventListener){  //现代浏览器
        document.addEventListener('DOMContentLoaded', function(){
            document.removeEventListener('DOMContentLoaded', arguments.callee, false);
            fn();
        }, false);
    }else if(document.attachEvent){  //低版本IE
        document.attachEvent('onreadystatechange', function(){
            if(document.readyState === 'complete'){
                document.detachEvent('onreadystatechange', arguments.callee);   
                fn();
            }
        })
    }else{
        window.onload = fn;
    }
}

 

 

总结

HTML文档加载步骤:

  1. 由上往下解析HTML结构。
  2. 遇到src属性则发起请求加载资源,只有script会阻塞HTML解析,其他(css、img等)都不会影响HTML解析。
  3. script资源加载完,执行JS脚本。
  4. DOM树构建完成,触发DOMContentLoaded
  5. 其他css、img、iframe等资源如果还未加载完成继续加载。
  6. 页面加载完毕,触发window.onload

为什么要强调css放头部,js放尾部

因为css样式表是浏览器渲染页面的重要一环,应该尽早发起请求加载,毕竟也不会阻塞HTML解析。

而HTML解析遇到script会阻塞,所以应该放到后面,而不阻塞其他资源请求。虽然说还是要等script加载执行完成之后才会触发DOMContentLoaded,但现在很多现代浏览器为了更好地用户体验,能够渲染不完整的dom树和cssom,尽快的减少白屏的时间。

参考文章    参考文章

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/234712.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 织梦程序做的网站,会员下载的弹窗问题

    织梦程序做的网站,会员下载的弹窗问题

    2021年9月25日
    41
  • Py之cv2:cv2库(OpenCV,opencv-python)的简介、安装、使用方法(常见函数、方法等)最强详细攻略[通俗易懂]

    Py之cv2:cv2库(OpenCV,opencv-python)的简介、安装、使用方法(常见函数、方法等)最强详细攻略[通俗易懂]Py之cv2:cv2库(OpenCV,opencv-python)的简介、安装、使用方法(常见函数、方法等)最强详细攻略目录关于OpenCV简介OpenCV应用领域1、计算机视觉领域方向2、计算机操作底层技术安装OpenCV的的两种方法T1、使用whl文件法T2、直接命令法T3、Anaconda环境下安装OpenCV常见函数、方法0、基本库函数1、图像基本运算2、Image.open和cv2.imread的区别及其转换相关文章:Py

    2022年6月23日
    39
  • spring事务的默认隔离级别_事务隔离级别有哪些

    spring事务的默认隔离级别_事务隔离级别有哪些目录1、前言2、验证结论3、总结1、前言事务的四个隔离级别想必大家都已经清楚,但是在学习Spring的时候,我们发现Spring自己也有四个隔离级别(加上默认的是五个)。那么问题来了,当Spring设置的隔离级别和我们在数据库设置的隔离级别不一致时,哪个会生效?先抛出结论:Spring设置的隔离级别会生效2、验证结论要验证结论很简单,我们只需要在spring事务注解上面配置不同的隔离级别就行了:DAO层实现类的两个方法pay方法是模拟事务A先查询一次数据,然后休眠两秒再查询一次数据

    2025年11月11日
    4
  • 教师职称考计算机模块,2015年教师职称计算机考试模块「建议收藏」

    教师职称考计算机模块,2015年教师职称计算机考试模块「建议收藏」【职考宝典】为广大考生归纳2015年职称计算机考试模块,让考生提前进入考试氛围,了解考试题型,职考宝典涵盖面积广,模拟题库包含Internet应用,word2003、2007,winXP、wpsoffice、excel2003、2007,ppt2003、2007等。2015年教师职称计算机考试模块【职考宝典】为广大考生归纳2015年职称计算机考试模块,让考生提前进入考试氛围,了解考试题型,职考…

    2022年6月2日
    33
  • NFS修改2049端口号

    NFS修改2049端口号欢迎大家搜素微信公众号“东面而视”,分项技术及讨论技术《什么是NFS》   就是通过网络共享目录,让网络上的其他服务器能够挂载访问共享目录内的数据。(一般共享视频,图片等静态数据) 为什么修改NFS的2049端口1. 其他服务再用2049端口2. 由于某些规定(如端口号必须在10000以上)修改方法1. 检查LINUX是否按装NFS[root@CT5_6-32-22…

    2022年6月27日
    30
  • RegisterStartupScript 过期

    RegisterStartupScript 过期1.ClientScriptManager.RegisterStartupScript方法:  1)  ClientScriptManager.RegisterStartupScript (Type,String,String)  2)  ClientScriptManager.RegisterStartupScript (Type,String,String,

    2022年7月20日
    17

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号