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,尽快的减少白屏的时间。

参考文章    参考文章

 

 

 

 

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • linux加静态路由命令,LINUX添加静态路由「建议收藏」

    linux加静态路由命令,LINUX添加静态路由「建议收藏」LINUX添加静态路由建议,先用命令添加,测试生效未有其他影响后,添加到配置文件中。以本次127前置添加静态路由为例用户需要添加路由如下,命令格式为windows添加格式。route-a-p10.113.70.0mask255.255.255.010.113.65.44route-a-p10.113.70.240mask255.255.255.24010.113.6…

  • uva 11732 – strcmp() Anyone? 不错的Trie题

    uva 11732 – strcmp() Anyone? 不错的Trie题

  • emwin显示中文的方法_emwin中文手册

    emwin显示中文的方法_emwin中文手册emwin显示中文

  • 山东大学舆情研究中心_舆情系统

    山东大学舆情研究中心_舆情系统项目结题总结一、项目背景二、技术要点三、功能介绍1、服务器端(1)启动服务器一、项目背景对于一所高校来说一个好的风评有着十分重要的作用,拥有一个良好的口碑,能吸引更多的生源、引进更多的人才,学校的综合素质能力也会因此提升,因此我们小组选择了《山东大学舆情分析系统》这一题目,通过搜集百度新闻、央视新闻、今日头条、齐鲁网、新浪、网易新闻、微博、知乎等网站的有关信息,对搜集到的文本信息所进行的分词、统计处理,将结果绘制成可视化的热度词条、情感倾向变化图,并实时展示在网站上,以此来更直观的了解山大的实时风评。

  • 双飞翼布局和圣杯布局

    双飞翼布局和圣杯布局实现左右固定宽度,中间自适应的布局(中间先加载渲染),代码如下<!DOCTYPEhtml><html> <head> <metacharset=”utf-8″/> <title>css</title> </head> <styletype=”text/css”> *…

  • rapidxml学习

    rapidxml学习参考:官网http://rapidxml.sourceforge.net/https://blog.csdn.net/wqvbjhc/article/details/7662931http://blog.sina.com.cn/s/blog_9b0604b40101o6fm.htmlrapidxml_print.hpp修改代码:#ifndefRAPIDXML_PRINT_HP…

发表回复

您的电子邮箱地址不会被公开。

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