W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
瀏覽器允許我們跟蹤外部資源的加載 —— 腳本,iframe,圖片等。
這里有兩個事件:
onload
? —— 成功加載,onerror
? —— 出現(xiàn) error。假設(shè)我們需要加載第三方腳本,并調(diào)用其中的函數(shù)。
我們可以像這樣動態(tài)加載它:
let script = document.createElement('script');
script.src = "my.js";
document.head.append(script);
……但如何運行在該腳本中聲明的函數(shù)?我們需要等到該腳本加載完成,之后才能調(diào)用它。
請注意:
對于我們自己的腳本,可以使用 JavaScript module,但是它們并未被廣泛應(yīng)用于第三方庫。
我們的得力助手是 ?load
? 事件。它會在腳本加載并執(zhí)行完成時觸發(fā)。
例如:
let script = document.createElement('script');
// 可以從任意域(domain),加載任意腳本
script.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"
document.head.append(script);
script.onload = function() {
// 該腳本創(chuàng)建了一個變量 "_"
alert( _.VERSION ); // 顯示庫的版本
};
因此,在 onload
中我們可以使用腳本中的變量,運行函數(shù)等。
……如果加載失敗怎么辦?例如,這里沒有這樣的腳本(error 404)或者服務(wù)器宕機(jī)(不可用)。
發(fā)生在腳本加載期間的 error 會被 error
事件跟蹤到。
例如,我們請求一個不存在的腳本:
let script = document.createElement('script');
script.src = "https://example.com/404.js"; // 沒有這個腳本
document.head.append(script);
script.onerror = function() {
alert("Error loading " + this.src); // Error loading https://example.com/404.js
};
請注意,在這里我們無法獲取更多 HTTP error 的詳細(xì)信息。我們不知道 error 是 404 還是 500 或者其他情況。只知道是加載失敗了。
重要:
onload
/onerror
事件僅跟蹤加載本身。
在腳本處理和執(zhí)行期間可能發(fā)生的 error 超出了這些事件跟蹤的范圍。也就是說:如果腳本成功加載,則即使腳本中有編程 error,也會觸發(fā)
onload
事件。如果要跟蹤腳本 error,可以使用window.onerror
全局處理程序。
load
和 error
事件也適用于其他資源,基本上(basically)適用于具有外部 src
的任何資源。
例如:
let img = document.createElement('img');
img.src = "https://js.cx/clipart/train.gif"; // (*)
img.onload = function() {
alert(`Image loaded, size ${img.width}x${img.height}`);
};
img.onerror = function() {
alert("Error occurred while loading image");
};
但是有一些注意事項:
<img>
? 是個例外。它要等到獲得 src (?*
?) 后才開始加載。<iframe>
? 來說,iframe 加載完成時會觸發(fā) ?iframe.onload
? 事件,無論是成功加載還是出現(xiàn) error。這是出于歷史原因。
這里有一條規(guī)則:來自一個網(wǎng)站的腳本無法訪問其他網(wǎng)站的內(nèi)容。例如,位于 https://facebook.com
的腳本無法讀取位于 https://gmail.com
的用戶郵箱。
或者,更確切地說,一個源(域/端口/協(xié)議三者)無法獲取另一個源(origin)的內(nèi)容。因此,即使我們有一個子域,或者僅僅是另一個端口,這都是不同的源,彼此無法相互訪問。
這個規(guī)則還影響其他域的資源。
如果我們使用的是來自其他域的腳本,并且該腳本中存在 error,那么我們無法獲取 error 的詳細(xì)信息。
例如,讓我們使用一個腳本 error.js
,該腳本只包含一個(錯誤)函數(shù)調(diào)用:
// error.js
noSuchFunction();
現(xiàn)在從它所在的同一個網(wǎng)站加載它:
<script>
window.onerror = function(message, url, line, col, errorObj) {
alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script src="/article/onload-onerror/crossorigin/error.js"></script>
我們可以看到一個很好的 error 報告,就像這樣:
Uncaught ReferenceError: noSuchFunction is not defined
https://javascript.info/article/onload-onerror/crossorigin/error.js, 1:1
現(xiàn)在,讓我們從另一個域中加載相同的腳本:
<script>
window.onerror = function(message, url, line, col, errorObj) {
alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script src="https://cors.javascript.info/article/onload-onerror/crossorigin/error.js" rel="external nofollow" rel="external nofollow" ></script>
此報告與上面那個示例中的不同,就像這樣:
Script error.
, 0:0
error 的詳細(xì)信息可能因瀏覽器而異,但是原理是相同的:有關(guān)腳本內(nèi)部的任何信息(包括 error 堆棧跟蹤)都被隱藏了。正是因為它來自于另一個域。
為什么我們需要 error 的詳細(xì)信息?
因為有很多服務(wù)(我們也可以構(gòu)建自己的服務(wù))使用 window.onerror
監(jiān)聽全局 error,保存 error 并提供訪問和分析 error 的接口。這很好,因為我們可以看到由用戶觸發(fā)的實際中的 error。但是,如果一個腳本來自于另一個源(origin),那么正如我們剛剛看到的那樣,其中沒有太多有關(guān) error 的信息。
對其他類型的資源也執(zhí)行類似的跨源策略(CORS)。
要允許跨源訪問,<script>
標(biāo)簽需要具有 crossorigin
特性(attribute),并且遠(yuǎn)程服務(wù)器必須提供特殊的 header。
這里有三個級別的跨源訪問:
crossorigin
? 特性 —— 禁止訪問。crossorigin="anonymous"
? —— 如果服務(wù)器的響應(yīng)帶有包含 ?*
? 或我們的源(origin)的 header ?Access-Control-Allow-Origin
?,則允許訪問。瀏覽器不會將授權(quán)信息和 cookie 發(fā)送到遠(yuǎn)程服務(wù)器。crossorigin="use-credentials"
? —— 如果服務(wù)器發(fā)送回帶有我們的源的 header ?Access-Control-Allow-Origin
? 和 ?Access-Control-Allow-Credentials: true
?,則允許訪問。瀏覽器會將授權(quán)信息和 cookie 發(fā)送到遠(yuǎn)程服務(wù)器。請注意:
你可以在 Fetch:跨源請求 一章中了解有關(guān)跨源訪問的更多信息。這一章描述了用于網(wǎng)絡(luò)請求的
fetch
方法,但策略是完全相同的。
諸如 “cookie” 之類的內(nèi)容超出了本章的范圍,但你可以在 Cookie,document.cookie 一章學(xué)習(xí)它們。
在我們的示例中沒有任何跨源特性(attribute)。因此,跨源訪問被禁止。讓我們來添加它吧。
我們可以在 "anonymous"
(不會發(fā)送 cookie,需要一個服務(wù)器端的 header)和 "use-credentials"
(會發(fā)送 cookie,需要兩個服務(wù)器端的 header)之間進(jìn)行選擇。
如果我們不關(guān)心 cookie,那么可以選擇 "anonymous"
:
<script>
window.onerror = function(message, url, line, col, errorObj) {
alert(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script crossorigin="anonymous" src="https://cors.javascript.info/article/onload-onerror/crossorigin/error.js" rel="external nofollow" rel="external nofollow" ></script>
現(xiàn)在,假設(shè)服務(wù)器提供了 Access-Control-Allow-Origin
header,一切都正常。我們有了完整的 error 報告。
圖片 <img>
,外部樣式,腳本和其他資源都提供了 load
和 error
事件以跟蹤它們的加載:
load
? 在成功加載時被觸發(fā)。error
? 在加載失敗時被觸發(fā)。唯一的例外是 <iframe>
:出于歷史原因,不管加載成功還是失敗,即使頁面沒有被找到,它都會觸發(fā) load
事件。
readystatechange
事件也適用于資源,但很少被使用,因為 load/error
事件更簡單。
通常,圖片在被創(chuàng)建時才會被加載。所以,當(dāng)我們向頁面中添加 <img>
時,用戶不會立即看到圖片。瀏覽器首先需要加載它。
為了立即顯示一張圖片,我們可以“提前”創(chuàng)建它,像這樣:
let img = document.createElement('img');
img.src = 'my.jpg';
瀏覽器開始加載圖片,并將其保存到緩存中。以后,當(dāng)相同圖片出現(xiàn)在文檔中時(無論怎樣),它都會立即顯示。
創(chuàng)建一個函數(shù) preloadImages(sources, callback)
,來加載來自數(shù)組 source
的所有圖片,并在準(zhǔn)備就緒時運行 callback
。
例如,這段代碼將在圖片加載完成后顯示一個 alert
:
function loaded() {
alert("Images loaded")
}
preloadImages(["1.jpg", "2.jpg", "3.jpg"], loaded);
如果出現(xiàn)錯誤,函數(shù)應(yīng)該仍假定圖片已經(jīng)“加載完成”。
換句話說,當(dāng)所有圖片都已加載完成,或出現(xiàn)錯誤輸出時,將執(zhí)行 callback
。
例如,當(dāng)我們計劃顯示一個包含很多圖片的可滾動圖冊,并希望確保所有圖片都已加載完成時,這個函數(shù)很有用。
在源文檔中,你可以找到指向測試圖片的鏈接,以及檢查它們是否已加載完成的代碼。它應(yīng)該輸出 300
。
算法:
img
?。onload/onerror
?。onload
? 或 ?onerror
? 被觸發(fā)時,增加計數(shù)器。callback()
?。Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: