zoukankan      html  css  js  c++  java
  • jQuery的document ready与 onload事件——你真的思考过吗?

    在进行实验和资料查询时,我遇到了几个关键问题:

    1. window.onload到底是什么加载完触发?

    2. body为什么会有onload事件?

    3. 为什么是window.onload,而不是document.onload?

    4. document ready到底是什么ready,DOM渲染完成?

    5. jQuery怎么实现$(document).ready?

    6. jQuery的ready,还能ready什么?

    7. jQuery的document ready就一定比window.onload快吗?

    8. 为什么外部script文件放页面内容后面好,是一定的吗?

    onload

    load是一个事件,会在页面包含文件资源加载完成后触发。

    支持该事件的HTML标签:

    body、frame、frameset、iframe、img、link、script。

    这里有两点我很在意的是:

    1. 其他标签是没有该事件,所以不要胡乱用。

    2. 就如我开头提的问题之一,为什么body会有onload?(Question 2)

    首先,我为什么有这个疑问,因为可以看出的是,支持该事件的其他标签都是为了加载标签里的src资源,而body显然不是。

    我在《JavaScript高级程序设计》找到一个合理的解释:

    一般来说,在window上面发生的任何事件都可以在body元素中通过相应的特性来指定,因为在HTML中无法访问window元素。这样是为了保证向后兼容的权宜之计,但所有浏览器都能很好地支持这种方式。

    结论:为了对应上window.onload,body标签才有onload。

    支持该事件的JavaScript对象:

    image、window。

    这里有两个问题我很在意:

    1. window.onload到底是什么加载完触发?(Question 1)

    依资料记载,当页面完全加载后(包括所有图像、JavaScript文件等外部资源),就会触发该事件。这方式跟在body上写onload效果是一样的。

    2. 为什么是window.onload,而不是document.onload? (Question 3)

    按上面那个问题的解释,明明是整个document里面完全加载后触发,那为什么是window.onload(在window上是什么鬼),而不是document.onload?

    还是按《JavaScript高级程序设计》上解释:

    根据“DOM2级事件”规范,应该是在document而非window上面触发load事件。但是,所有浏览器都在window上面实现了该事件,以确保向后兼容。

    结论:道理是这么道理,但大家都这么干了。

    jQuery document ready

    从原生js来讲,并没有ready这种事件。

    那么 document ready到底是什么ready了(Question 4)

    按资料说明,这个事件指的是文档结构(DOM)加载完成触发的。

    PS:这解释还算合理,就放过这问题。

    那jQuery怎么实现$(document).ready(Question 5)

    下面我尝试解析jquery3.0.0版本(兼容IE9+,现代浏览器)里面关于ready事件的实现!

    注意:

    版本我选择比较新的3.0.0,相比于较旧版本(例1.x)的,里面的实现会简单一些,因为舍弃一些兼容代码。不过实现的原理是一样,倒不需要多个版本都详看。

    原理:

    在jquery脚本加载的时候,会监听DOMContentLoaded事件(监听load是补救后路)。当事件触发时候,会执行ready事件的回调。

    代码:

    var readyList = jQuery.Deferred();
     
    //保存所有DOM加载完后执行的函数。
    jQuery.fn.ready = function( fn ) {
         readyList.then( fn );
         return this;
    };
     
    jQuery.extend( {
     
         //标记DOM ready事件是否触发过。
         isReady: false,
     
         // A counter to track how many items to wait for before
         // the ready event fires. See #6781
         readyWait: 1,
     
         // Hold (or release) the ready event
         holdReady: function( hold ) {
              if ( hold ) {
                   jQuery.readyWait++;
              } else {
                   jQuery.ready( true );
              }
         },
     
         // Handle when the DOM is ready
         ready: function( wait ) {
     
              // Abort if there are pending holds or we're already ready
              if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
                   return;
              }
     
              // Remember that the DOM is ready
              jQuery.isReady = true;
     
              // If a normal DOM Ready event fired, decrement, and wait if need be
              if ( wait !== true && --jQuery.readyWait > 0 ) {
                   return;
              }
     
              // If there are functions bound, to execute
              readyList.resolveWith( document, [ jQuery ] );
         }
    } );
     
    jQuery.ready.then = readyList.then;
     
    // The ready event handler and self cleanup method
    function completed() {
         document.removeEventListener( "DOMContentLoaded", completed );
         window.removeEventListener( "load", completed );
         jQuery.ready();
    }
     
    // Catch cases where $(document).ready() is called
    // after the browser event has already occurred.
    // Support: IE <=9 - 10 only
    // Older IE sometimes signals "interactive" too soon
    if ( document.readyState === "complete" ||
         ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
     
         // Handle it asynchronously to allow scripts the opportunity to delay ready
         window.setTimeout( jQuery.ready );
     
    } else {
         // Use the handy event callback
         document.addEventListener( "DOMContentLoaded", completed );
     
         //其实最奸诈是这里,在不支持DOMContentLoaded事件的浏览器,用load事件代替
         // A fallback to window.onload, that will always work
         window.addEventListener( "load", completed );
    }

    特别地方:

    1. 注册事件是用addEventListener,所以该jquery版本应该是只支持IE9+。

    2. jQuery的ready,还能ready什么?(Question 6)

    ready函数仅能用于当前document,因此无需选择器,所以不能ready其他元素。

    三种姿势使用该函数:

    $(document).ready(function)
    $().ready(function)
    $(function)

    谁更快?

    jQuery的document ready就一定比window.onload快吗?(Question 7)

    我写了一个例子来实验:

    <!DOCTYPE HTML>
    <html lang="en-US">
    <head>
    <meta charset="UTF-8"/>
    <title>加载时机</title>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"
    onload="console.log('jquery.js loaded')"></script>
    <script>
         console.log('define functions');
         function load(type, info){
              console.log(type + ' onload ' + (info || ""), new Date().getTime());
         }
         $(document).ready(function () {
              load('document ready');
         });
         document.onload = function () {
              load('document');
         };    
         window.onload = function () {
              load('window');
         };
         window.addEventListener("load",function(){
              load('window addEventListener');
         });
         document.addEventListener( "DOMContentLoaded", function () {
              load('DOMContentLoaded');
         });
    </script>
    </head>
         <body onload="load('body')">
              <div onload="load('text')">test</div>
              <img onload="load('img',1)" src="http://www.deskcar.com/desktop/else/2013714232149/17.jpg" />
              <img onload="load('img',2)" src="http://www.deskcar.com/desktop/else/2013714232149/16.jpg" />
              <script onload="load('js')" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.0/react.min.js"></script>
         </body>
    </html>

    这种大体有两种结果:

    首次加载:

    二次加载:

    第一种情况非常符合我们的想法,ready比onload快,顺序也比较合理。

    而第二种情况就有些怪异,应该依照上面jquery ready事件的实现,那ready应该要DOMContentLoaded后面啊。我思来想去,我觉得这是个误会,由于二次加载时利用到缓存,导致文件资源都很快加载,各个事件触发的时间非常相近,顺序也不定,就给人一种ready顺序不对之感,大家应该发现这几个事件都是在几十毫秒之内触发。

    PS:js执行需要时间,几十毫秒不同的顺序我觉得很正常。另个尝试几次,二次加载顺序确实会有变化,但时间都很相近。

    所以,jQuery的document ready不一定比window.onload快执行。

    为什么外部script文件放页面内容后面好?

    script执行顺序:

    《JavaScript高级程序设计》说过——无论如何包含代码,只要不存在defer和async属性,浏览器都会按照<script>元素在页面中出现的先后顺序对它们依次进行解析。换句话说,在第一个<script>元素包含的代码解析完成后,第二个<script>包含代码才会被解析,然后才是第三个.....

    放head元素里:

    <!DOCTYPE HTML>
    <html lang="en-US">
    <head>
    <meta charset="UTF-8"/>
    <title>Example</title>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.0/react.min.js"></script>
    </head>
         <body>
        
         </body>
    </html>

    在head元素里包含所有JavaScript文件,就必须等到全部JavaScript代码都被下载、解析和执行完成以后,才能呈现页面的内容(浏览器在遇到<body>标签时才开始呈现内容)。在需要很多JavaScript文件时候,浏览器呈现页面会出现明显的延迟,延时期间浏览器是一片空白。

    所以,外部script文件放页面内容后面。这样,在解析JavaScript代码之前,页面内容将完全呈现出来。

    一定是放页面内容后面吗?

    有种情况是JavaScript放哪里都一样的,那就是内容是依赖JavaScript的执行渲染时候,放哪都一样。

    总结

    虽然这篇文章是简单的问题,但有时我们就是连简单的东西都没搞懂,还以为我们懂了。


    本文为原创文章,转载请保留原出处,方便溯源,如有错误地方,谢谢指正。

    本文地址 :http://www.cnblogs.com/lovesong/p/5641834.html

  • 相关阅读:
    【BZOJ4538】[Hnoi2016]网络 整体二分+树状数组
    【BZOJ4543】[POI2014]Hotel加强版 长链剖分+DP
    【BZOJ1304】[CQOI2009]叶子的染色 树形DP
    【BZOJ4552】[Tjoi2016&Heoi2016]排序 二分+线段树
    【BZOJ4557】[JLoi2016]侦察守卫 树形DP
    【BZOJ4499】线性函数 线段树
    【BZOJ1576】[Usaco2009 Jan]安全路经Travel 最短路+并查集
    【BZOJ4560】[JLoi2016]字符串覆盖 KMP+状压DP
    【BZOJ2124】等差子序列 树状数组维护hash值
    MDX导航结构层次:《Microsoft SQL Server 2008 MDX Step by Step》学习笔记九
  • 原文地址:https://www.cnblogs.com/lovesong/p/5641834.html
Copyright © 2011-2022 走看看