在使用Javascript操作HTML DOM時,記得要養成網頁載入後才動手的好習慣,不然會有可怕的事發生… (可參考邊做邊學 jQeury 系列 3-jQuery 常用的 Javascript 技巧教學影片,在5分02秒的示範)

在jQuery中,要做到"等網頁載入後再執行",我們有兩種選擇:

$(document).ready(fn)$(window).load(fn)

二者的差別在於$(document).ready(fn)發生在"網頁本身的HTML"載入後就觸發,而$(window).load(fn)則會等到"網頁HTML標籤中引用的圖檔、內嵌物件(如Flash)、IFrame"等拉哩拉雜的東西都載入後才會觸發。

我寫了一段示範程式突顯二者的區別。由於Sky_angmap_4.jpg是一張1.2MB的大型圖檔,實際執行時,可以觀察到 “ready event!” –> 圖檔緩慢顯現 -> "load event!”的過程。(嫌下載時間太短,差別不夠明顯的話,可以試試Sky_angmap_7.jpg,XD)

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript"
     src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js">
</
script>
    <script type="text/javascript">
        $(function() {
            alert("ready event!");
        });
        $(window).load(function() {
            alert("load event!");
        });
    </script>
</head>
<body>
<img src="http://blender-archi.tuxfamily.org/images/Sky_angmap_4.jpg" />
</body>
</html>

一般來說,等網頁全部元素都載入才執行程式時機有點晚,因為在此之前,使用者已經可以點選操作網頁,跳脫我們程式的掌控範圍。因此,我們幾乎都是將程式放在$(document).ready(function() { ... })中(即$(function() { … }))。

$(window).load(fn)適合執行一些要等待圖檔或元素全部載入才可進行的動作,例如: 檢查圖檔長寬。換句話說,上回的大圖自動縮小Script可以透過$(window).load(fn)改寫得更簡單,完全不用擔心圖檔沒下載完成時的特殊處理,算是一個很好的應用範例。

    function GM_wait() {
        if(typeof unsafeWindow.jQuery == 'undefined') 
            window.setTimeout(GM_wait,100);
        else 
        { 
            $ = unsafeWindow.jQuery; 
            $(window).load(resizeHugeImage);
        }
    }
    GM_wait();
 
    // All your GM code must be inside this function
    function resizeHugeImage() {
        $("img").each(function() {
            var img = $(this);
            var w = img.width(), h = img.height();
            var longSide = (w > h ? w : h);
    //...以下省略...

Comments

# by A*lan~

黑大,以您對jQuery實戰經驗以及了解,有考慮出書嗎? 針對主題性或是大補帖之類的實戰文章,因為您的文字精要簡潔有趣勵志真是不可多得的良師啊!之前我學Regular Expression只看了您寫的PDF內有RE使用規則和三個簡短範例,只花一個晚上就學會了,真的是一個範例勝過千言萬語啊。只要您出書,不管內容是什麼,我一定買,您可以做個問卷嗎?問問大家的意見,預購的話可以附上您的親筆簽名嗎?

# by Jeffrey

to A*lan, 每每看到讀者支持的留言,總是讓我熱淚盈眶... @&#^$ <---不是罵髒話,是淚水滴進Keyboard秀逗了 XD 當一個兼職書籍作家需要過人的體力與毅力,還有要犠牲掉一部分生活品質、家庭幸福甚至身體健康的心理準備,縱使熱血滿懷,年屆不惑的中年人顧忌的事還挺多的,只能暫且打消念頭。但各位朋友的情義相挺,小弟點滴在心頭,由衷感激~~~ 寫寫Blog或雜誌文章只需犠牲幾個清晨的睡眠,還在中年人僅存的一丁點熱血可以硬撐的範圍內,目前就只會採行這種模式與大家同樂吧! 既然提到了 "只要您出書,不管內容是什麼,我一定買" --> 真巧,要不要參考一下這個http://www.plurk.com/p/yqg38

# by HowieWen

若是需要判斷兩個iframe裡面文件的高度來設定iframe高度該怎麼做呢?小弟目前用很笨的 $("iFrame#Frame_Left").load(function() { $("iFrame#Frame_Right").load(function() { var LeftHeight=document.frames("Frame_Left").document.body.scrollHeight+20; var RightHeight=document.frames("Frame_Right").document.body.scrollHeight+20; if (LeftHeight>=RightHeight){ $("iFrame#Frame_Left").height(LeftHeight); $("iFrame#Frame_Right").height(LeftHeight); } else{ $("iFrame#Frame_Left").height(RightHeight); $("iFrame#Frame_Right").height(RightHeight); } }); }); 應該有很大的改進空間吧?!

# by lupus

有個疑問,如果是引用js檔,那應該是ready或者load呢? <script language="javascript" src="xx.js"></script>

# by 嵐曦

你好 想請教 我掛載了一個js $("window").load(function() { var a = function() { ... }; }); 如果想在html執行這個fn 像這樣 <a href="#" id="run" onClick="a()">123</a> 應該怎麼寫? 我這樣是錯誤的ˊˋ

# by Jeffrey

to 嵐曦, 我會寫成$(function() { $("#run").click(function() { ... }); }); 如果你一定要用onClick="a()"的話,請寫成window.a = function() { ... },或直接在$("window")上方寫成function a() { ... }

# by hosting

真的是一個範例勝過千言萬語啊

# by 小黑

小弟今天測試想要在 $(window).load 中去修改 iframe 標籤的 title 屬性值 可是 jQuery 確實有執行到,可是觀看 原始碼, 卻又沒有沒有被設定到,怪哉? 請黑大指點迷津

# by Jeffrey

to 小黑,你的原始碼是用瀏覽器右鍵選單叫出來檢視的嗎? 那一分是當初載入的版本,並不會反應Javascript動態變更過的結果,建議你用IE Dev Tools之類的工具來觀察,才會看到即時的結果。(參考: http://blog.darkthread.net/post-2011-08-04-ie9-dev-tools-debug-css.aspx)

# by ATTIE

請問一下,我使用ThickBox去開另一個頁面(是使用iFrame的方式),但我被開啟的頁面裡面有在用另一個jQuery去做ScrollBar,現在發現一開啟就會說"必須要有物件",錯誤的指向就是去呼叫jQuery的函式。 請問我要怎麼修改程式碼呢??? 謝謝。 這是我在iFrame內呼叫ScrollBar的程式碼: <script language="javascript" type="text/javascript"> $(window).load(function () { $("#mcs_container").mCustomScrollbar("vertical", 200, "easeOutCirc", 1.25, "fixed", "yes", "no", 0); }); </script>

# by Jeffrey

to ATTIE, 由你提供的程式碼片段不太能確定問題所在,"必須要有物件"多半發生在某個應該要有傳的變數傳回null的情境,建議先確認出現null的來源在哪裡會比較容易找到問題根源。(另外順便提醒,Iframe所內嵌的網頁來自同一網站嗎? 會不會與跨網域限制有關?)

# by Sam

黑大您好^^ 小弟遇到一個很冏的情況... 我的頁面會載入許多不同domain的js檔案, 而這些js檔案會改變頁面上的內容 當內容改變後, 我要在$(window).load裡面調整該內容的css 但是在ie9...load卻偷跑了Orz 不知道有沒有什麼建議> <

# by Jeffrey

to Sam, 依我的理解,load會等待圖檔、Iframe等都載入完成才觸發,但如果所謂"js檔案會改變頁面上的內容"是透過操作DOM完成,就可能在load()後才執行,違背預期的順序。 你對那些不同Domain的js有控制權嗎? 如果有,我建議把修改css的程式定義成自訂事件,例如: $(document).bind("changeCss", function() { ... }),而在js改變頁面內容後加一行程式碼觸發該事件$(document).trigger("changeCss")。 若無權要求js配合,那我想到的辦法是透過Polling的技巧定期去檢查js搞定了沒,搞定了再去動CSS。

# by 小黑

請問黑大,請問有什麼方法可以測試看出哪個事件方法被優先呼叫,比方說,我要測試 body onload 事件與 ready 事件誰最先被呼叫?

# by Jeffrey

to 小黑, 我想到最簡單方法是在load事件中alert("Load Event"),在ready事件中alert("Ready Event"),看哪一個訊息先跳出來。(或者用console.log也可以)

# by 小楊

感謝黑大的文章 想請問有什麼方法可以像 $(document).ready事件一樣 可以重覆寫而不會覆蓋前面的呢(同名FUNC)? EX: function a(){ alert("1"); } function a(){ alert("2"); } 希望同時輸出 1和2 謝謝!

# by Jeffrey

to 小楊, 如果是我,會考慮用jQuery自訂Event實現: var eventName = "myEvent"; $(document).bind(eventName, function() { alert("Event1"); }); $(document).bind(eventName, function() { alert("Event2"); }); $(document).bind(eventName, function() { alert("Event3"); }); $(document).trigger(eventName);

# by 小楊

感謝黑大的指導,感激不盡!

# by jerry

看到您的blog, 嚷我有一種溺水者抓到救生圈的感覺 想請教,在jQuery裡面設定的變數, 可以被javascript引用嗎? 例如在 js 中宣告 $(document).ready(function(){ var textA="ABC"; }); 在html中 <script language='javascript'>document.write(textA);</script> 這時會發現無法顯示 testA 的值, 錯誤原因是 testA is not defined 因為韌體端的關係,我必須使用這種方式呼叫,請教我可以這樣呼叫 jQuery 的變數嗎?

# by Jeffrey

to jerry, .ready()發生在網頁載入完整後,其時機晚於你所提的document.write(textA),所以關鍵主要在時機而非範圍。 依我的理解,js中寫成window.textA="ABC";且拉到.ready()外直接執行應可解決問題,或是另有隱情?

# by Jerry

拉到 ready() 外面確實是可以執行, 但是原本廠商提供的 code 使用介面很難用, 對方又不願意改, 想要重新寫, 原本 code 的參數都寫在 ready() 裏面, 所以才會碰到以上的問題, 這個問題網路上似乎找不到解決方案.

# by Jeffrey

to Jerry, 如果Code只能加在ready()中沒有第二種選擇,而<script>document.wirte()</script>又不可憾動,我認為此題無解。

# by 牛大神

<script>var textA = '';</script> ... jQuery Loaded ... $(document).ready(function(){ var textA="ABC"; }); ... <script defer="defer">document.wirte(textA)</script>

# by 牛大神

以上方法完美解決了外部和內部的相容性問題 defer="defer" 內的指令會在 $(document).ready() 後執行

# by Jeffrey

to 牛大神,學習了,謝謝分享。

Post a comment