JS頁面事件(非常詳細)

2020-07-16 10:05:07
所有頁面事件都明確的處理整個頁面的函數和狀態。主要包括頁面的載入和解除安裝,即使用者存取頁面和離開關閉頁面的事件型別。

頁面初始化

load 事件型別在頁面完全載入完畢的時候觸發。該事件包含所有的圖形影象、外部檔案(如 CSS、JS 檔案等)的載入,也就是說,在頁面所有內容全部載入之前,任何 DOM 操作都不會發生。為 window 物件系結 load 事件型別的方法有兩種。

1) 直接為 window 物件註冊頁面初始化事件處理常式。
window.onload = f;
function f() {
    alert("頁面載入完畢");
}

2) 在頁面 <body> 標籤中定義 onload 事件處理屬性。
<body onload="f()">
<script>
    function f() {
        alert("頁面載入完畢");
    }
</script>

範例1

如果同時使用上面兩種方法定義頁面初始化事件型別,它們並不會發生衝突,也不會發生兩次觸發事件。
<body onload="f()">
<script>
    window.onload = f;
    function f() {
        alert("頁面載入完畢");
    }
</script>
</body>
原來 JavaScript 直譯器在編譯時,如果發現同時使用兩種方法定義 load 事件型別,會使用 window 物件註冊的事件處理常式覆蓋掉 body 元素定義的頁面初始化事件屬性。

範例2

在下面範例中,函數 f2() 被呼叫,而函數 f1() 就被覆蓋掉。
<body onload="f1()">
<script>
    window.onload = f2;
    function f1 () {
        alert('<body onload="f1()">');
    }
    function f2 () {
        alert('window.onload = f2;');
    }
</script>
</body>
在實際開發中,load 事件型別經常需要呼叫附帶引數的函數,但是 load 事件型別不能夠直接呼叫函數,要解決這個問題,有以下兩種方法。

1) 在 body 元素中通過事件屬性的形式回撥函數。
<body onload="f('Hi')">
<script>
    function f (a) {
        alert(a);
    }
</script>
</body>

2) 通過函數巢狀或閉包函數來實現。
window.onload = function () {  //事件處理常式
    f("Hi");  //呼叫函數
}
functioin f(a) {  //被處理常式
    alert(a);
}
也可以採用閉包函數形式,這樣在註冊事件時,雖然呼叫的是函數,但是其返回值依然是一個函數,不會引發語法錯誤。
window.onload = f("Hi");
function f(a) {
    return function () {   
        alert(a);
    }
}
通過這種方法,可以實現在 load 事件型別上系結更多的響應回撥函數。
window.onload = function () {
    f1();  //係結響應函數1
    f2();  //係結響應函數2
}
function f1() {
    alert("f1()");
}
function f2() {
    alert("f2()");
}
但是,如果分別系結 load 事件處理常式,則會相互覆蓋,最終只能夠有一個系結響應函數被呼叫。
window.onload = f1();
function f1(){
    alert("f1()");
}
function f2() {
    alert("f2()");
}
也可以通過事件註冊的方式來實現。
if (window.addEventListener) {  //相容DOM標準
    window.addEventListener ("load", f1, false);  //為load新增事件處理常式
    window.addEventListener ("load", f2,false);  //為load新增事件處理常式
} else {  //相容IE事件模型
    window.attachEvent ("onload", f1);
    window.attachEvent ("onload", f2);
}

結構初始化

在傳統事件模型中,load 是頁面中最早被觸發的事件。不過當使用 load 事件來初始化頁面時可能會存在一個問題,就是當頁面中包含很大的檔案時,load 事件需要等到所有影象全部載入完成之後才會被觸發。也許使用者希望某些指令碼能夠在頁面結構載入完畢之後就能夠被執行。要怎麼辦呢?

這時可以考慮使用 DOMContentLoaded 事件型別。作為 DOM 標準事件,它是在 DOM 文件結構載入完畢的時候觸發的,因此要比 load 事件型別先被觸發。目前,Mozilla 和 Opera 新版本已經支援了該事件。而 IE 和 Safari 瀏覽器還不支援。

範例1

如果在標準 DOM 中,可以這樣設計。
<script>
    window.onload = f1;
    if (document.addEventListener) {
        document.addEventListener ("DOMContentLoaded", f, false);
    }
    function f () { alert("我要提前執行了"); }
    function f1 () { alert("頁面初始化完畢"); }
</script>
<img src="Winter.jpg">
這樣,在圖片載入之前會彈出“我要提前執行了”的提示資訊,而當圖片載入完畢之後會彈出“頁面初始化完畢”。這說明在頁面 HTML 結構載入完畢之後觸發 DOMContentLoaded 事件型別,也就是說,在文件標籤載入完畢時觸發該事件並呼叫函數 f(),然後,當文件所有內容載入完畢(包括圖片下載完畢)時才觸發 load 事件型別,並呼叫函數 f1()。

範例2

由於 IE 事件模型不支援 DOMContentLoaded 事件型別,為了實現相容處理,需要運用一點小技巧,即在文件中寫入一個新的 script 元素,但是該元素會延遲到檔案最後載入。然後,使用 Script 物件的 onreadystatechange 方法進行類似的 readyState 檢查後及時呼叫載入事件。
if (window.ActiveXObject) {  //相容IE事件模型
    document.write ("<script id=ie_onload defer src=javascript:void(0)></script");  //寫入指令碼標籤
    document.getElementById("ie_load").onreadystatechange = function () {
        //判斷指令碼標籤的狀態
        if (this.readyState == "complete") {  //如果狀態為完成,則說明文件結構載入已完畢
            this.onreadystatechange = null;  //清空當前反腐
            f();  //呼叫預先執行的回撥函數
        }
    }
}
寫入的 <script> 標籤中包含了 defer屬性,defer 表示“延期”的意思,使用 defer 屬性可以讓指令碼在整個頁面裝載完成之後在解析,而非邊載入邊解析。這對於只包含事件觸發的指令碼來說,可以提高整個頁面的載入速度。與 src 屬性聯合使用,還可以使這些指令碼在後台被下載,而前台的內容正常顯示給使用者。目前只有 IE 事件模型支援該屬性。當定義了 defer 屬性後,<script> 標籤中就不應包含 document.write 命令,因為 document.write 將產生直接輸出的效果,而且不包含任何立即執行指令碼要使用的全域性變數或者函數。

<script> 標籤在文件結構載入完畢之後才載入,於是,只要判斷它的狀態就可以確定當前文件結構是否已經載入完畢並觸發響應的事件。

範例3

針對 Safari 瀏覽器,可以使用 setInterval() 函數週期性的檢查 document 物件的 readyState 屬性,隨時監控文件是否載入完畢,如果完成則呼叫回撥函數。
if (/WebKit/i.test(navigator.userAgent)) {  //相容Safari瀏覽器
    var _timer = setInterval (function () {  //定義時間監測器
        if (/loaded|complete/.test(document.readyState) {  //如果當前狀態顯示完成
            clearInterval(_timer);  //清空時間監測器
            f();  //呼叫預先執行的回撥函數
        }
    }, 10);
}
把上面 3 段條件合併在一起即可實現相容不同瀏覽器的 DomContentLoaded 事件處理常式。

頁面解除安裝

unload 表示解除安裝的意思,這個事件在從當前瀏覽器視窗內移動文件的位置時觸發,也就是說,通過超連結、前進或後退按鈕等方式詞能夠一個頁面跳轉到其他頁面,或者關閉瀏覽器視窗時觸發。

範例

下面函數的提示資訊將在解除安裝頁面時發生,即在離開頁面或關閉視窗前執行。
window.onunload = f;
function f() {
    alert("888");
}
在 unload 事件型別中無法有效阻止預設行為,因為該事件結束後,頁面將不復存在。由於在視窗關閉或離開頁面之前只有很短的時間來執行事件處理常式,所以不建議使用該事件型別。使用該事件型別的最佳方式是取消該頁面的物件參照。

beforeunload 事件型別與 unload 事件型別的功能相近,不過它更人性化,如果 beforeunload 事件處理常式返回字串資訊,那麼該字串會顯示在一個確認對話方塊中,詢問使用者是否離開當前頁面。例如,執行下面的範例,當重新整理或關閉頁面時會彈出以下提示資訊。
window.beforeunload = function (e) {
    return "您的資料還沒有儲存!";
}
演示結構如下: