HTML笔记 ·

window的onload事件和domcontentloaded执行顺序

onload事件支持

我们首先来看一下都有哪些支持onload事件。

  • 支持该事件的 HTML 标签:<body>, <frame>, <frameset>, <iframe>, <img>, <link>, <script>;
  • 支持该事件的 JavaScript 对象:image, layer, window

对于这些标签比如iframeimgscript标签,image对象等等,我们用的很多,都是在相应的元素加载完成之后执行的事件。下面我们讨论一下
window.onload、DOMContentLoaded的执行顺序问题。

window.onload、DOMContentLoaded

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>oecom.cn</title>
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script language="javascript">
        window.onload = haha();
        function haha(){console.log(document.getElementById("div1"));}
    </script>
</head>
<body>
    <div id="div1">a</div>
</body>
</html>

window的onload事件和domcontentloaded执行顺序
window的onload事件和domcontentloaded执行顺序
window的onload事件和domcontentloaded执行顺序

上述三个图分别为chrome edge和Firefox,我们发现他们的结果都是一样的,先执行documentloded事件,然后再执行window.onload事件。所以说一般情况下,DOMContentLoaded事件要在window.onload之前执行,当DOM树构建完成的时候就会执行DOMContentLoaded事件。当window.onload事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。

window.onload和body中onload

我们在写代码的过程中经常也会在body标签上添加onload,那么documentlodedwindow.onload和body中onload哪一个会先执行哪一个会后执行呢,下面我们来看一下示例代码。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>oecom.cn</title>
   <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script language="javascript">
        window.onload = haha;
        function haha(){console.log("window.onload执行完成");}
        if(document.addEventListener){
            function DOMContentLoaded(){
                console.log("DOMContentLoaded执行完成");
            }
            document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
        }
    </script>
</head>
<body onload="console.log('bodyonload');">
    <div id="div1">a</div>
</body>
</html>

还是对三种主流浏览器测试,经过测试后我发现‘window.onload执行完成’这句话三个浏览器都没有执行,只是输出了‘bodyonload’,所以我们得出了一个结论就是body的onload事件会覆盖掉window.onload事件。

那么我们吧script放到后面又会是什么情况呢

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>oecom.cn</title>
   <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
</head>
<body onload="console.log('bodyonload');">
    <div id="div1">a</div>
    <script language="javascript">
        window.onload = haha;
        function haha(){console.log("window.onload执行完成");}
        if(document.addEventListener){
            function DOMContentLoaded(){
                console.log("DOMContentLoaded执行完成");
            }
            document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
        }
    </script>
</body>
</html>

经过测试发现输出结果为下:
DOMContentLoaded
window.onload

无论是将script标签放到body标签内部还是放到body下面执行结果都是一样的。所以我们得出一个结论就是window.onload和body的onload事件谁在下面会执行谁。

jQuery的load事件

$(document).ready()或者$(function(){})是经常使用的,其原理都是使用了类似DOMContentLoaded。来看一下jq官方的解释:

The .ready() method offers a way to run JavaScript code as soon as the page's Document Object Model (DOM) becomes safe to manipulate. This will often be a good time to perform tasks that are needed before the user views or interacts with the page, for example to add event handlers and initialize plugins. When multiple functions are added via successive calls to this method, they run when the DOM is ready in the order in which they are added. As of jQuery 3.0, jQuery ensures that an exception occuring in one handler does not prevent subsequently added handlers from executing.

Most browsers provide similar functionality in the form of a DOMContentLoaded event. However, jQuery's .ready() method differs in an important and useful way: If the DOM becomes ready and the browser fires DOMContentLoaded before the code calls .ready( handler ), the function handler will still be executed. In contrast, a DOMContentLoaded event listener added after the event fires is never executed.

Browsers also provide the load event on the window object. When this event fires it indicates that all assets on the page have loaded, including images. This event can be watched in jQuery using $( window ).on( "load", handler ). In cases where code relies on loaded assets (for example, if the dimensions of an image are required), the code should be placed in a handler for the load event instead.

Note that although the DOM always becomes ready before the page is fully loaded, it is usually not safe to attach a load event listener in code executed during a .ready() handler. For example, scripts can be loaded dynamically long after the page has loaded using methods such as $.getScript(). Although handlers added by .ready() will always be executed in a dynamically loaded script, the window's load event has already occurred and those listeners will never run.

在此我简单翻译一下:

.ready()只要页面的文档对象模型(DOM)可以安全地操作,该方法就提供了一种运行JavaScript代码的方法。这通常是在用户查看或与页面交互之前执行所需任务的好时机,例如添加事件处理程序和初始化插件。当通过对此方法的连续调用添加多个函数时,它们在DOM按照添加顺序准备就绪时运行。从jQuery 3.0开始,jQuery确保在一个处理程序中发生的异常不会阻止随后添加的处理程序执行。

大多数浏览器以事件的形式提供类似的功能DOMContentLoaded。但是,jQuery的.ready()方法以一种重要且有用的方式不同:如果DOM准备就绪并且DOMContentLoaded在代码调用之前浏览器触发.ready( handler ),则该函数handler仍将被执行。相反,DOMContentLoaded事件触发后添加的事件侦听器永远不会执行。

浏览器还在对象load上提供事件window。当此事件触发时,表示页面上的所有资源都已加载,包括图像。可以在jQuery中使用查看此事件$( window ).on( "load", handler )。如果代码依赖于加载的资源(例如,如果需要图像的尺寸),则应将代码放在load事件的处理程序中。

注意,尽管DOM总是在页面完全加载之前就绪,但是在 .ready()处理程序期间执行的代码中附加加载事件侦听器通常不安全。例如,可以在使用诸如$.getScript()的方法加载页面很久之后动态加载脚本。尽管由 .ready() 添加的处理程序总是在动态加载的脚本中执行,但是窗口的加载事件已经发生,并且这些侦听器永远不会运行。

为什么我上面说是类似于DOMContentLoaded,看了上面官方文档的解释应该会明白了。在jq官方文档中也说明了:

  • $( handler )
  • $( document ).ready( handler )
  • $( "document" ).ready( handler )
  • $().ready( handler )

上述几种方式的作用是相同的。并且执行会依次执行不会覆盖。我们来做一个例子:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
</head>
<body onload="console.log('bodyonload');">
    <div id="div1">a</div>
<img src="http://cdn.oecom.cn/oecom/dom/effect-cube6.jpg">
 <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
   <script>
     $(document).ready(function(){
            console.log("jq document ready1");
        })
        window.onload = haha;
        function haha(){console.log("window.onload执行完成");}
        if(document.addEventListener){
            function DOMContentLoaded(){
                console.log("DOMContentLoaded执行完成");
            }
            document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
        }
       $(function(){
            console.log("jq document ready3");
        })
        $().ready(function(){
            console.log("jq document ready2");
        })
    </script>
</body>
</html>

上面这段代码输出的结果始终是
window的onload事件和domcontentloaded执行顺序
但是你千万不要以为ready事件始终是在window.onload后才执行的,也千万不要以为他始终DOMContentLoaded事件之后执行,因为下面这段代码就会打破你的认知。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
  <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css">
<!-- 可选的Bootstrap主题文件(一般不用引入) -->
<link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap-theme.min.css">
</head>
<body onload="console.log('bodyonload');">
    <div id="div1">a</div>
<img src="http://cdn.oecom.cn/oecom/dom/effect-cube6.jpg">
 <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
 <!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="http://cdn.bootcss.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
   <script>
     $(document).ready(function(){
            console.log("jq document ready1");
        })
        window.onload = haha;
        function haha(){console.log("window.onload执行完成");}
        if(document.addEventListener){
            function DOMContentLoaded(){
                console.log("DOMContentLoaded执行完成");
            }
            document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
        }
       $(function(){
            console.log("jq document ready3");
        })
        $().ready(function(){
            console.log("jq document ready2");
        })
    </script>
</body>
</html>

在这段代码中我添加了很多在线的js和css,通过输出我发现了不一样的结果:
window的onload事件和domcontentloaded执行顺序

所以jq的ready事件执行结束时间和DOMContentLoaded结束时间并不是完全相同的,所以在使用过程中应当进行一些注意。

参与评论