JS 实现 jQuery的$(function(){});

1、浏览器渲染引擎的HTML解析流程

何谓“渲染”,其实就是浏览器把请求到的HTML内容显示出来的过程。渲染引擎首先通过网络获得所请求文档的内容,通常以8K分块的方式完成。下面是渲染引擎在取得内容之后的基本流程:

1,解析html以构建dom树(构建DOM节点):渲染引擎开始解析html,并将标签转化为内容树中的dom节点。

2,构建render树(解析样式信息):解析外部CSS文件及style标签中的样式信息。Render树由一些包含有各种属性的矩形组成,它们将被按照正确的顺序显示到屏幕上。

3,布局render树(布局DOM节点):执行布局过程,它将确定每个节点在屏幕上的确切坐标。

4,绘制render树(绘制DOM节点):Render树构建好了之后,将会再下一步就是绘制,即遍历render树,并使用UI后端层绘制每个节点。

以上就是HTML渲染的基本流程,但这并不包含解析过程中浏览器加载外部资源如图片、脚本、iframe等的过程。说白了,上面的四步仅仅是HTML结构的渲染流程,而外部资源的加载在HTML结构的渲染流程中贯穿始终,即便绘制DOM节点已经完成,外部资源依然可能正在加载中或尚未加载。

 

2、window.onload

了解了浏览器渲染引擎的HTML解析流程,我们就回到domReady。前文提到了,那个蛋疼的TypeError是由于在DOM树构建完成之前对节点进行了操作,而通常的解决的办法就是让js在window.onload的回调里执行,也就是说,在文档所有的解析渲染、资源加载完成之前,不让js脚本执行,这样一来就妥妥地避免了因js操作先于DOM树创建而带来的bug:

1 Window.onload = function(){
2 //doSomething
3 }

这样的解决办法应该是初学原生js时很多人最常用的解决办法,看起来也的确没什么问题。如果文档外部资源不多的时候也没什么问题,但,我们来做一个假设。假设一个页面上有100张远程图片,我需要让js做到在点击每张图片时alert出图片的src属性,又该怎么做?

是不是已经发现点小问题了?按照第二部分内容对浏览器解析渲染HTML流程的介绍,DOM树很快就构建完毕了,而100张图片还在缓慢地加载。而要想执行alert出图片src属性的js,则需要等到100张图片全部加载完成后才能执行。而在这期间,页面元素不会响应你的任何操作,就好像“死”了一样。如果是在实际项目中,用户很可能不会等到你页面所有东东加载完以后才去操作,在面对一个不会对自己的操作做任何响应的页面,唯一比较解气的方式就是——果断关掉~然后……就没有了然后。

所以在实际应用中,我们经常会遇到这样的场景,让页面加载后去做一些事情:绑定事件、DOM操作某些结点等。使用window.onload对于很多实际的应用而言有点太“迟”了,比较影响用户体验。那有没有更好的方法解决这个问题?比如提前到只要DOM树创建完成之后就可以进行如上操作呢?答案当然是有的:DOMContentLoaded事件

 

3、DOMContentLoaded

说这个之前必须要提一下jQuery中的domReady机制。很多时候在使用jq也会出现最前面出现的那个TypeError,解决办法就是把js放到jQuery的ready回调里:

1 $(document).ready(function(){...});

或者:

1 $(function(){...});

这样一来,错误妥妥地没了。然后对比因果关系,大概得出一个结论:jQuery的ready回调应该跟window.onload的效果原理是一样的。恩,应该是这样。那我们就先来看一看jQuery(1.11.1)的ready回调是如何实现的:

复制代码
 1 jQuery.fn.ready = function( fn ) {2     // Add the callback3     jQuery.ready.promise().done( fn );4     return this;5 };6 jQuery.ready.promise = function( obj ) {7     if ( !readyList ) {8         readyList = jQuery.Deferred();9         if ( document.readyState === "complete" ) {
10             setTimeout( jQuery.ready );
11         } else if ( document.addEventListener ) {
12             document.addEventListener( "DOMContentLoaded", completed, false );
13             window.addEventListener( "load", completed, false );
14         } else {
15             document.attachEvent( "onreadystatechange", completed );
16             window.attachEvent( "onload", completed );
17             var top = false;
18             try {
19                 top = window.frameElement == null && document.documentElement;
20             } catch(e) {}
21             if ( top && top.doScroll ) {
22                 (function doScrollCheck() {
23                     if ( !jQuery.isReady ) {
24                         try {
25                             // Use the trick by Diego Perini
26                             top.doScroll("left");
27                         } catch(e) {
28                             return setTimeout( doScrollCheck, 50 );
29                         }
30                         detach();
31                         jQuery.ready();
32                     }
33                 })();
34             }
35         }
36     }
37     return readyList.promise( obj );
38 };
复制代码

看起来比想象中的window.onload要复杂呵。Jq的源码中出现了DOMContentLoaded、readyState、onreadystatechange,这些跟domReady有什么关系?

我们还是先从DOMContentLoaded说起吧。就如前面所述,很多时候我们会把js逻辑写在window.onload回调中,以防DOM树还没有建完就开始对节点进行操作从而导致错误,而对于很多实际应用来说,越早介入对DOM的干涉就越好,比如进行特征侦测、事件绑定、DOM操作神马的。domReady还可以满足用户提前绑定事件的需求,因为有些情况下页面的图片等外部资源过多,window.onload迟迟不能触发,这时若还没有绑定事件,用户点任何的按钮都没反应(链接除外)会直接影响体验。

为了解决window.onload的短板,FF中便增加了一个DOMContentLoaded方法,与onload相比,DOMContentLoaded方法触发的时间更早,它是在页面的DOM树创建完成后(也就是HTML解析第一步完成)即触发,而无需等待其他资源的加载。Webkit引擎从版本525(Webkit nightly 1/2008:525+)开始也引入了该事件,Opera中也包含该方法。到目前为止NB的IE仍然没有要添加的意思。虽然IE下没有,但解决办法总是有的。于是对于那些忙前忙后的兼容小达人和死不悔改的顽固派,也就有了两套策略:

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

2)不支持的,就用来自Diego Perini发现的著名Hack兼容。兼容原理大概就是,通过IE中的document.documentElement.doScroll(‘left’)来判断DOM树是否创建完毕。

Blabla了这么多,来看个IE模拟DOMContentLoaded例子吧。这个例子就来自上面发现IE下doScroll Hackd的作者,细看也就是简化版的jQuery.ready回调的IE处理逻辑。

复制代码
 1 function IEContentLoaded (w, fn) {2     var d = w.document, done = false,3     // 只执行一次用户的回调函数init()4     init = function () {5         if (!done) {6             done = true;7             fn();8         }9     };
10     (function () {
11         try {
12             // DOM树未创建完之前调用doScroll会抛出错误
13             d.documentElement.doScroll('left');
14         } catch (e) {
15             //延迟再试一次~
16             setTimeout(arguments.callee, 50);
17             return;
18         }
19         // 没有错误就表示DOM树创建完毕,然后立马执行用户回调
20         init();
21     })();
22     //监听document的加载状态
23     d.onreadystatechange = function() {
24         // 如果用户是在domReady之后绑定的函数,就立马执行
25         if (d.readyState == 'complete') {
26             d.onreadystatechange = null;
27             init();
28         }
29     };
30 }                
复制代码

而对于高大上的chrome、ff等高级浏览器来说,对DOMContentLoaded事件的处理就相对来说小case了,按照标准的事件绑定方式就可以处理:

1 if ( document.addEventListener ) {
2     document.addEventListener( "DOMContentLoaded", completed, false );
3 }

 

五、实例

看到这,想必大家已经对DOMContentLoaded已经有了新的认识,onload保险丝也该适时换成智能电门啦~接下来就来个鲜活的例子,来让大家更清晰的做下对比。

首先,页面上有一组图片:

复制代码
1 <ul>
2     <li><img src="img/01.jpg" /></li>
3     <li><img src="img/02.jpg" /></li>
4     <li><img src="img/03.jpg" /></li>
5     <li><img src="img/04.jpg" /></li>
6     <li><img src="img/05.jpg" /></li>
7 </ul>
复制代码

页面的js处理逻辑:

复制代码
 1 <script>2     var d = document;3     var msgBox = d.getElementById("showMsg");4     var imgs = d.getElementsByTagName("img");5     var time1 = null,time2 = null;6     if(d.addEventListener){7         d.addEventListener("DOMContentLoaded",domReady,false);8     }else{9         IEContentLoaded(domReady);
10     }
11     function domReady(){
12         msgBox.innerHTML += "dom已加载!<br>";
13         time1 = new Date().getTime();
14         msgBox.innerHTML += "时间戳:" + time1 + "<br>";
15     }
16      
17     //兼容IE的domReady
18     function IEContentLoaded(fn){
19         var done = false,
20         init = function(){
21             if(!done){
22                 done = true;
23                 fn();
24             }
25         };
26         (function(){
27             try {
28                 d.documentElement.doScroll('left');
29             }catch(e){
30                 setTimeout(arguments.callee,50);
31                 return;
32             }
33             init();
34         })(); 
35         d.onreadystatechange = function(){
36             msgBox.innerHTML += "加载状态:" + d.readyState + "<br>";
37             if(d.readyState == 'complete'){
38                 d.onreadystatechange = null;
39             }
40         }
41     }
42     window.onload = function(){
43         msgBox.innerHTML += "onload已加载!<br>";
44         time2 = new Date().getTime();
45         msgBox.innerHTML += "时间戳:" + time2 + "<br>";
46         msgBox.innerHTML +="domReady比onload快:" + (time2 - time1) + "ms<br>";
47     }
48 </script>
复制代码

相信js脚本不用做过多解释,在前面都已做过详细分析,我们直接来看运行结果:

很容易就能看出,DOMContentLoaded执行5238ms之后才执行的onload。这只是一个DEMO的差距,而如果是更大型的应用,可能这个时间差距会更大。

 

转载于:https://www.cnblogs.com/wang985850293/p/5231400.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/573943.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html 分页_MySQL——优化嵌套查询和分页查询

Java识堂&#xff0c;一个高原创&#xff0c;高收藏&#xff0c;有干货的微信公众号&#xff0c;欢迎关注优化嵌套查询嵌套查询(子查询)可以使用SELECT语句来创建一个单列的查询结果&#xff0c;然后把这个结果作为过滤条件用在另一个查询中。嵌套查询写起来简单&#xff0c;也…

从原理上搞定编码-- Base64编码

开发者对Base64编码肯定很熟悉&#xff0c;是否对它有很清晰的认识就不一定了。实际 上Base64已经简单到不能再简单了&#xff0c;如果对它的理解还是模棱两可实在不应该。大概介绍一下Base64的相关内容&#xff0c;花几分钟时间就可以彻底理解它。文 章下边贴了一个Base64的编…

docker mysql总是退出_Docker提升测试效率之路

现如今&#xff0c;Docker已经成为了很多公司部署应用、服务的首选方案。依靠容器技术&#xff0c;我们能在不同的体系结构之上轻松部署几乎任何种类的应用。作为测试一方&#xff0c;我们应与时俱进&#xff0c;将Docker容器技术应用到测试工作中。为了让小伙伴们可以快速上手…

32位mysql安装包_软件测试基础——Linux系统搭建MySQL数据库

一、mysql下载1. 下载&#xff1a;官方网址:https://dev.mysql.com/downloads/mysql/2. 选择相应的版本&#xff0c;由于cenos是基于红帽的&#xff0c;所以Select Operating System选择Red Hat...。我所用的镜像为cenos7所以Red Hat....linux7&#xff0c;一定要选相应的版本&…

python gevent模块 下载_Python中的多任务,并行,并发,多线程,多进程,协程区别...

多任务CPU承担了所有的计算任务。一个CPU在一个时间切片里只能运行一个程序。当我们想同时运行多于一个程序的时候&#xff0c;就是多任务&#xff0c;例如同时运行微信&#xff0c;QQ&#xff0c;浏览器等等。多任务的目的是提升程序的执行效率&#xff0c;更充分利用CPU的资源…

vue-router 路由嵌套显示不出来_网络协议|OSI模型第三层网络层中的路由

的IP协议OSI第二层中用以太网协议定义了信息传输单元&#xff0c;简称为帧&#xff0c;它长这个样子。同样的在OSI第三层中&#xff0c;会用 IP 协议去定义信息传输单元&#xff0c;简称为数据包&#xff0c;它长这个样子。实际上&#xff0c;最终在网络上传输的是第二层的帧&a…

asp.net怎么实现按条件查询_【33期】分别谈谈联合索引生效和失效的条件

点击上方“Java面试题精选”&#xff0c;关注公众号面试刷图&#xff0c;查缺补漏>>号外&#xff1a;往期面试题&#xff0c;10篇为一个单位归置到本公众号菜单栏->面试题&#xff0c;有需要的欢迎翻阅。这道题考查索引生效条件、失效条件。像这类问题才其实很有意义&…

java 二分搜索获得大于目标数的第一位_程序员数据结构算法编程,二分查找搜索算法的原理与应用介绍!...

本文来讲一种搜索算法&#xff0c;即二分搜索算法&#xff0c;通常在面试时也会被问到。我们先来看一个例子&#xff0c;在图书馆通常是根据查到的编号去找书&#xff0c;可以在书架上按顺序一本本地查找&#xff0c;也可以找到一本书不符合预期时&#xff0c;再跳过一大部分书…

2020idea插件怎么同步_VScode 插件整理

1、auto rename tag &#xff1a;HTML 标签自动闭合&#xff1b;避免了在整个页面中费劲查找。你想将一个H2标签更改为H3标签&#xff0c;或者你想将一个div标签更改为span标签&#xff0c;不管要做什么&#xff0c;你都要浪费时间来查找结束标签&#xff0c;这时候就该用这个插…

python 将两幅图拼接_清华王教授典藏的python电子书,整整10个G拿去不谢

终于拿到&#xff01;清华王教授典藏的电子书&#xff0c;整整10个G&#xff01;兄弟&#xff0c;毫无套路&#xff01;无偿获取方式:1.点赞评论2.关注小编&#xff0c;私信“Python”(点开头像就能看到私信按钮啦).Python指南——五行代码实现批量抠图你是否曾经想将某张照片中…

地磅称重软件源码_【漯河衡器】导致地磅称重不准原因及处理措施

地磅是一种新型的大型电子衡器&#xff0c;能够迅速、直观、高准确度地展现工商业、仓储、货站贸易计量的重要工具。做为贸易结算的工具&#xff0c;地磅的可靠性、准确性、科学性有着极为重要的影响。而在货物来往中&#xff0c;地磅是等价交换的桥梁&#xff0c;一旦地磅显现…

寻宝机器人电路板焊接_专业维修淮安市KUKA库卡KRC2机器人回收{机器人调试}

FANUC机器人伺服-023故障排除&#xff1a;FANUCR-2000六轴焊接机器人点焊进程中&#xff0c;J4机械臂显现自动滑动故障&#xff0c;机器人发出伺服故障报警&#xff0c;报警故障码为伺服-023&#xff0c;依据FANUC机器人维修手册&#xff0c;故障代码解释以下&#xff1a;伺服误…

android uber启动动画,仿 Uber 视频背景登录界面以及登录动画

现在有越来越多的 app 的登录/注册界面的背景是播放视频或者 gif&#xff0c;我主要看了 Uber 和 keep 的登录界面再配合拉勾的登录界面仿作了一个登录界面。1.首先&#xff0c;查资料我在 github 上找到了这两个库&#xff1a;-STLBGVideo 这个库是 oc 写的&#xff0c;但你的…

遍历列表python_Python 遍历List的三种方法

转载至https://www.cnblogs.com/pizitai/p/6398276.html #!/usr/bin/env python # -*- coding: utf-8 -*- if __name__ __main__: list [html, js, css, python] # 方法1 print 遍历列表方法1&#xff1a; for i in list: print ("序号&#xff1a;%s 值&#xff1a;%s&…

名图1.8智能隐藏功能_自动打包不脏手才是真智能,双11销冠,拓牛自动打包垃圾桶体验...

不是吧&#xff01;不是吧&#xff01;都是2020年了&#xff0c;不会还有人不知道电动垃圾桶的存在吧&#xff1f;如果你还不知道&#xff0c;那就跟5G智玩好物一起来看看最新上线的拓牛T Air Lite吧。首先&#xff0c;让我们看一下拓牛T Air Lite的外观。在大众的印象里&#…

android struts2 图片上传,xhEditor struts2实现图片上传

如果想要出现上传按钮,在xhEditor设置以下参数&#xff1a;html5Upload : false //此属性必须为falseupImgUrl : "ImgUpload.action" //上传服务器接口onUpload : insertUpload //服务器返回信息,JSON格式一、前台代码$(function() {…

python如何画出多个独立的图片_python实现在一个画布上画多个子图

matplotlib 是可以组合许多的小图, 放在一张大图里面显示的. 使用到的方法叫作 subplot. 均匀画图 使用import导入matplotlib.pyplot模块, 并简写成plt. 使用plt.figure创建一个图像窗口. 1 2 3 import matplotlib.pyplot as plt plt.figure() 使用plt.subplot来创建小图. plt.…

e5cc温控仪通讯参数设定_产品介绍||DeltaWiFi通讯型多功能电表DPMC520W

Delta-WiFi通讯型多功能电表DPM-C520W台达WiFi通讯型多功能电表DPM-C520W &#xff0c;使用无线通信技术&#xff0c;非常适合于挑高空间和不易配线的盘体使用。可支持各种电力参数量测及谐波量测&#xff0c;支持报警设定、参数群组化设定&#xff0c;采用MODBUS-TCP协议&…

文件设置索引_什么样的网站结构备受搜索引擎喜爱?

网站在优化中&#xff0c;对于收录问题也非常重视&#xff0c;但很多时候&#xff0c;网站各方面都做的很好但就是蜘蛛不抓取&#xff0c;这个时候就要考虑是网站结构问题了&#xff0c;导致网站页面无法被正常抓取。那么怎样才能提升搜索引擎的抓取呢&#xff1f;什么样的结构…

函数运行 形参实参变化 内存空间_可能python创始人都不知道的,python函数实参形参讲解...

今天我们来学习一下python函数的用法函数是带名字的代码块&#xff0c;我们可以直接调用函数&#xff0c;无需反复板鞋完成该函数的代码1.下面我们来看一个简单的函数首先定义一个函数&#xff0c;def是定义的意思&#xff0c;后面是函数名在函数中同样使用缩进来区分语句是否在…