問題
專案中經常會遇到這樣的問題:當某個js
指令碼載入完成後再執行相應任務,但很多朋友可能並不知道怎麼判斷我們要載入的js
檔案是否載入完成,如果沒有載入完成我們就呼叫js
檔案裡面的函式是不會成功的。本文主要講解怎麼在成功載入js
檔案後再執行相應回撥任務。
基本思路
我們可以動態的建立 <script>
元素,然後通過更改它的src
屬性來載入指令碼,但是怎麼知道這個指令碼檔案載入完成了呢?因為有些函式需要在指令碼載入完成才能呼叫。IE 瀏覽器中可以使用 <script>
元素的 onreadystatechange
來監控載入狀態的改變,並通過判斷它的 readyState
是 loaded
或 complete
來判斷指令碼是否載入完成。而非 IE 瀏覽器可以使用 onload
來直接判斷指令碼是否載入完成。
動態指令碼簡單示例
一個簡單的實現過程如下:
// IE下:
var HEAD = document.getElementsByTagName(`head`)[0] || document.documentElement
var src = `http://xxxxxx.com`
var script = document.createElement(`script`)
script.setAttribute(`type`,`text/javascript`)
script.onreadystatechange = function() {
if(this.readyState === `loaded` || this.readyState === `complete`) {
console.log(`載入成功!`)
}
}
script.setAttribute(`src`, src)
HEAD.appendChild(script)
複製程式碼
// Chrome等現代瀏覽器:
var HEAD = document.getElementsByTagName(`head`)[0] || document.documentElement;
var src = `http://xxxxxx.com`
var script = document.createElement(`script`)
script.setAttribute(`type`,`text/javascript`)
script.onload = function() {
console.log(`載入成功!`)
}
script.setAttribute(`src`, src)
HEAD.appendChild(script)
複製程式碼
原理很簡單,根據這兩個簡單的原理,我們進行一些修改,我把改成了兩個函式,分別是序列載入和並行載入。
序列和並行動態指令碼
當傳一個包含多個JS檔案路徑的陣列時,序列載入函式從第一個指令碼檔案載入開始,每載入成功一個便開始載入下一個指令碼檔案,全部載入完成後執行回撥函式。而並行載入是一開始便載入全部的指令碼檔案,也就是他們從同一點開始載入,當全部載入完成後,執行回撥函式。
/**
* 序列載入指定的指令碼
* 序列載入[非同步]逐個載入,每個載入完成後載入下一個
* 全部載入完成後執行回撥
* @param {Array|String} scripts 指定要載入的指令碼
* @param {Function} callback 成功後回撥的函式
* @return {Array} 所有生成的指令碼元素物件陣列
*/
function seriesLoadScripts(scripts, callback) {
if(typeof(scripts) !== `object`) {
var scripts = [scripts];
}
var HEAD = document.getElementsByTagName(`head`)[0] || document.documentElement;
var s = [];
var last = scripts.length - 1;
//遞迴
var recursiveLoad = function(i) {
s[i] = document.createElement(`script`);
s[i].setAttribute(`type`,`text/javascript`);
// Attach handlers for all browsers
// 非同步
s[i].onload = s[i].onreadystatechange = function() {
if(!/*@cc_on!@*/0 || this.readyState === `loaded` || this.readyState === `complete`) {
this.onload = this.onreadystatechange = null;
this.parentNode.removeChild(this);
if(i !== last) {
recursiveLoad(i + 1);
} else if (typeof(callback) === `function`) {
callback()
};
}
}
// 同步
s[i].setAttribute(`src`, scripts[i]);
HEAD.appendChild(s[i]);
};
recursiveLoad(0);
}
/**
* 並行載入指定的指令碼
* 並行載入[同步]同時載入,不管上個是否載入完成,直接載入全部
* 全部載入完成後執行回撥
* @param {Array|String} scripts 指定要載入的指令碼
* @param {Function} callback 成功後回撥的函式
* @return {Array} 所有生成的指令碼元素物件陣列
*/
function parallelLoadScripts(scripts, callback) {
if(typeof(scripts) !== `object`) {
var scripts = [scripts];
}
var HEAD = document.getElementsByTagName(`head`)[0] || document.documentElement;
var s = [];
var loaded = 0;
for(var i = 0; i < scripts.length; i++) {
s[i] = document.createElement(`script`);
s[i].setAttribute(`type`,`text/javascript`);
// Attach handlers for all browsers
// 非同步
s[i].onload = s[i].onreadystatechange = function() {
if(!/*@cc_on!@*/0 || this.readyState === `loaded` || this.readyState === `complete`) {
loaded++;
this.onload = this.onreadystatechange = null;
this.parentNode.removeChild(this);
if(loaded === scripts.length && typeof(callback) === `function`) callback();
}
};
// 同步
s[i].setAttribute(`src`,scripts[i]);
HEAD.appendChild(s[i]);
}
}
複製程式碼
在這裡是把 <script>
標籤動態的插入到頁面中的 <head>
標籤內部,並且載入完成後標籤元素會被自動移除。
使用方法
這裡宣告瞭一個陣列變數,裡面包含了兩個遠端的JS檔案地址(當然 <script>
標籤呼叫指令碼是支援跨域的):
var scripts = [
"http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
"http://wellstyled.com/files/jquery.debug/jquery.debug.js"
];
// 這兩個檔案分別是 jQuery 1.4.的庫檔案和 jQuery Debug 外掛
// 然後你可以使用下面的方法呼叫並在成功後執行回撥了。
parallelLoadScripts(scripts, function() {
/*
debug = new $.debug({
posTo : { x:`right`, y:`bottom` },
width: `480px`,
height: `50%`,
itemDivider : `<hr>`,
listDOM : `all`
});
*/
console.log(`指令碼載入完成啦`);
});
複製程式碼