jQuery $(document).ready()和JavaScript onload事件

聖騎士wind發表於2015-02-06

jQuery $(document).ready()和JavaScript onload事件

 

Why we need a right time? 

  對元素的操作和事件的繫結需要等待一個合適的時機,可以看下面的例子:

<!DOCTYPE html>
<meta charset="utf-8">
<html>

<head>
    <title>1-1</title>
    <script type="text/javascript">
        document.getElementById("panel").onclick = function () {
            alert("元素已經載入完畢 !");
        }
        /*執行錯誤*/
    </script>
</head>

<body>
<div id="panel">click me.</div>
</body>

</html>
wrong case

  如果這樣,還沒有等待元素載入完就繫結事件,

  瀏覽器Console中會報錯:
  TypeError: document.getElementById(...) is null
 
  更改一下時機,下面三個程式都是可以成功繫結事件的,點選元素之後會彈出相應的alert().
  第一種是把事件繫結放在body裡,元素之後:
<!DOCTYPE html>
<meta charset="utf-8">
<html>

<head>
    <title>1-2</title>
</head>

<body>
<div id="panel">click me.</div>
<script type="text/javascript">
    document.getElementById("panel").onclick = function () {
        alert("元素已經載入完畢 !");
    }
    /*正確執行*/
</script>
</body>

</html>
right way 1: in body

 

  第二種是放在window.onload中進行事件繫結:
<!DOCTYPE html>
<meta charset="utf-8">
<html>

<head>
    <title>1-3</title>
    <script type="text/javascript">
        window.onload = function () {
            document.getElementById("panel").onclick = function () {
                alert("元素已經載入完畢 !");
            }
        }
    </script>
</head>

<body>
<div id="panel">click me.</div>
</body>

</html>
right way 2: window.onload

 

  第三種是jQuery的ready()方法傳入繫結事件的方法:
<!DOCTYPE html>
<meta charset="utf-8">
<html>

<head>
    <title>Panel</title>
    <script src="jquery-1.11.2.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            document.getElementById("panel").onclick = function () {
                alert("元素已經載入完畢 !");
            }
        })
    </script>
</head>

<body>
<div id="panel">click me.</div>
</body>

</html>
right way 3: ready()

 

jQuery $(document).ready()和window.onload

  根據ready()方法的API說明http://api.jquery.com/ready/
  這個方法接收一個function型別的引數ready(handler), 方法的作用是: Specify a function to execute when the DOM is fully loaded.
  即當DOM載入完畢的時候,執行這個指定的方法.
  因為只有document的狀態ready之後,對page的操作才是安全的.
  $(document).ready()僅在DOM準備好的時候執行一次.
 
  與之相比,load事件會等到頁面渲染完成執行,即等到所有的資源(比如圖片)都完全載入完成的時候.
  $(window).load(function(){…})會等整個頁面,不僅僅是DOM,還包括影象和iframes都準備好之後,再執行.
  而ready()是在DOM準備好之後就執行了,即DOM樹建立完成的時候.所以通常ready()是一個更好的時機.
 
  如果DOM初始化完成之後再呼叫ready()方法,傳入的新的handler將會立即執行.
 
  注意:ready()方法多次呼叫,傳入的handler方法會串聯執行(追加).
  而JavaScript中,window.onload是賦值一個方法,即後面的會覆蓋掉前面的.
 
  看例子:
<!DOCTYPE html>
<meta charset="utf-8">
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <script src="jquery-1.11.2.js" type="text/javascript"></script>
    <script type="text/javascript">
        var startTime = new Date().getTime();
        $(document).ready(function () {
            var readyTime = new Date().getTime() - startTime;
            $("<div>jQuery的ready() : " + readyTime + " ms</div>").appendTo("body");
        })
        $(document).ready(function () {
            var readyTime2 = new Date().getTime() - startTime;
            $("<div>jQuery的ready() 2 : " + readyTime2 + " ms</div>").appendTo("body");
        })
        window.onload = function () {
            var windowOnLoadTime = new Date().getTime() - startTime;
            $("<div>window.onload : " + windowOnLoadTime + " ms</div>").appendTo("body");
        }
        window.onload = function () {
            var windowOnLoadTime2 = new Date().getTime() - startTime;
            $("<div>window.onload 2 : " + windowOnLoadTime2 + " ms</div>").appendTo("body");
        }
    </script>
</head>

<body>
<img src="http://www.google.com.hk/logos/2011/cezanne11-hp.jpg" style="width:200px;height:200px;"/>
</body>

</html>
 
    在瀏覽器中開啟顯示如下:
  例子說明並驗證了前面提到的兩個問題:
  1.jQuery的ready()方法呼叫時機比window.onload提前.
  window.onload要等待頁面中的資源(影象)載入完成,而ready()在DOM樹建立好之後就呼叫了.
  2.ready()方法呼叫後,傳入的方法是追加的形式,所以兩個方法都呼叫了;
  而window.onload是賦值的形式,所以後面的方法覆蓋了前面的方法.
 

$(document).ready()的三種簡寫

  $(document).ready()有三種方式來寫,它們是等價的:
  $( document ).ready( handler )
  $().ready( handler ) (this is not recommended)
  $( handler )

  因為.ready()方法僅在當前document的jQuery物件上才可以呼叫,所以selector可以被省略.

 

window物件和document物件

  Window物件表示瀏覽器中開啟的視窗: http://www.w3school.com.cn/jsref/dom_obj_window.asp
  Document物件表示載入瀏覽器的HTML文件: http://www.w3school.com.cn/jsref/dom_obj_document.asp
 

Event物件

  Event即事件,代表了各種狀態:http://www.w3school.com.cn/jsref/dom_obj_event.asp
  事件控制程式碼使我們可以在事件發生的時候附加一些操作和處理,比如按鈕點選事件發生的時候,進行什麼什麼操作.
  上面的參考連結中含有一個屬性列表,對應各種事件,可以利用這些屬性定義事件的行為.
  本文關注的onload就是其中一個事件.
 

onload事件

  onload事件是在載入完成後立即發生.(注意其中的l是小寫).
  支援該事件的標籤是:
  <body>, <frame>, <frameset>, <iframe>, <img>, <link>, <script>
  支援該事件的JavaScript物件是:
  image, layer, window.
  注意這裡並沒有document.
 
onload使用解析
  最常用的就是window.onload, 會等到整個頁面及各種資源都載入完成之後再執行後面賦值的function行為.
  另外,可以在標籤中使用onload,比如:
<body onload="inlineBodyOnloadTimeCounter();">

 

  其中inlineBodyOnloadTimeCounter()是一個自定義的JavaScript function.
  注意jQuery ready()的API文件中有這麼一段: 
The .ready() method is generally incompatible with the attribute. If load must be used, either do not use .ready() or use jQuery's .load() method to attach load event handlers to the window or to more specific items, like images.

  說明ready()方法和<body onload=“”>是不相容的.

 
  看下面的例子:
<!DOCTYPE html>
<meta charset="utf-8">
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <script src="jquery-1.11.2.js" type="text/javascript"></script>
    <script type="text/javascript">
        var startTime = new Date().getTime();
        $(document).ready(function () {
            var readyTime = new Date().getTime() - startTime;
            $("<div>jQuery的ready() : " + readyTime + " ms</div>").appendTo("body");
        })
        window.onload = function () {
            var windowOnLoadTime = new Date().getTime() - startTime;
            $("<div>window.onload : " + windowOnLoadTime + " ms</div>").appendTo("body");
        }

        function inlineBodyOnloadTimeCounter() {
            var bodyOnLoadTime = new Date().getTime() - startTime;
            $("<div>body onload: " + bodyOnLoadTime + " ms</div>").appendTo("body");
        }
    </script>
</head>

<body onload="inlineBodyOnloadTimeCounter();">
<img src="http://www.google.com.hk/logos/2011/cezanne11-hp.jpg" style="width:200px;height:200px;"/>
</body>
</html>
 
  在Chrome中:
 
  在FireFox中:
 
  說明Chrome中<body onload=“”>和window.onload是兩個分開的事件;
  而FF中,<body onload=“”>會覆蓋掉window.onload中的方法.
 
 
  再加上一個document.body.onload:
<!DOCTYPE html>
<meta charset="utf-8">
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <script src="jquery-1.11.2.js" type="text/javascript"></script>
    <script type="text/javascript">
    var startTime = new Date().getTime();
    $(document).ready(function() {
        var readyTime = new Date().getTime() - startTime;
        $("<div>jQuery的ready() : " + readyTime + " ms</div>").appendTo("body");
    })
    window.onload = function() {
        var windowOnLoadTime = new Date().getTime() - startTime;
        $("<div>window.onload : " + windowOnLoadTime + " ms</div>").appendTo("body");
    }
    if (document.body) {
        /*
        document.body.onload = function() {
            var documentBodyOnLoadTime = new Date().getTime() - startTime;
            $("<div>document.body.onload() : " + documentBodyOnLoadTime + " ms</div>").appendTo("body");
        }
        */
        //This codes will not be executed.
    } else {
        console.log("=======document.body doesn't exist!=======");
    }

    function inlineBodyOnloadTimeCounter() {
        var bodyOnLoadTime = new Date().getTime() - startTime;
        $("<div>body onload: " + bodyOnLoadTime + " ms</div>").appendTo("body");
    }
    </script>
</head>

<body onload="inlineBodyOnloadTimeCounter();">
    <img src="http://www.google.com.hk/logos/2011/cezanne11-hp.jpg" style="width:200px;height:200px;" />
    <script type="text/javascript">
    console.log("script in body!");
    if (document.body) {
        console.log("====document.body exist now!====");
        document.body.onload = function() {
            var documentBodyOnLoadTime = new Date().getTime() - startTime;
            $("<div>document.body.onload: " + documentBodyOnLoadTime + " ms</div>").appendTo("body");
        }
    } else {
        console.log("no document.body!");
    }
    </script>

</body>


</html>
 
  有下面幾點發現:
  1.在執行<head>中的指令碼時,document.body是null,只有當執行到<body>中的指令碼段,document.body才不是null.
  這時候才能給其onload賦值.
  console中的log如下:
"=======document.body doesn't exist!======="
"script in body!"
"====document.body exist now!===="

 

  2.在Chrome中開啟的時候:
  jQuery的ready() : 20 ms
  body onload: 207 ms
  document.body.onload: 208 ms
  前面一個例子顯示了ready(),window.onload和body onload,說明此時document.body.onload覆蓋了window.onload.
 
  3.在FireFox中開啟顯示:
  jQuery的ready() : 10 ms
  document.body.onload: 229 ms
  前面一個例子顯示ready()和body onload,說明此時document.body.onload又覆蓋了body onload. 
  另外單獨測試了一下,的確document.body.onload會覆蓋window.onload,程式碼略去.
  
 
結論:
在Chrome中:
  ready()
  window.onload==document.body.onload
  <body onload=“">
 
在FireFox中:
  ready()
  window.onload==<body onload=“”>==document.body.onload
 
  此分析的結果跟瀏覽器,甚至瀏覽器版本相關,本文寫於2015.2.6
  Chrome Version: 40.0.2214.94
  FireFox Version: 35.0
 

參考資料

  jQuery API ready(): http://api.jquery.com/ready/
 
 

相關文章