很久沒有遇到這問題了,如果有需要動態 inject 外部的 js 到你的 html 裡面,而且又需要使用外部的 js 的 function 時,該怎麼做才能確保在 js load 完之後,才能夠執行外部 js 的 function 呢?

起因

這算是蠻容易遇到的問題,常常會認為在動態載入 javascript 之後,就可以直接使用它,像是下面的例子:

外部的 https://www.eample.com/test.js

1
2
3
var test = () => {
console.log('i am test');
};

你打算動態的載入這 js,並執行它:
test.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
// dynamic load js
(function(d, id) {

if (d.getElementById(id)) {
return;
}

let fjs = d.getElementsByTagName('script')[0];
let js = d.createElement('script');

js.id = id;
js.src = "https://www.eample.com/test.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'testId'));

//execute external js
test();
</script>

會發現到有時候 https://www.eample.com/test.jstest() 有時候可以執行,有時候不行,如果網路速度過慢的話,還會以為完全不能執行。
原因就出在於瀏覽器載入 test.js 的時候可能還沒完成,就已經先執行 test()

解決方式

所以要使用 script 的 onload event 確保在 test.js 載入完畢後才能執行 function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
let scriptOnload = () => {
//execute external js
test();
};

// dynamic load js
(function (d, id, callBack) {

if (d.getElementById(id)) {
return;
}

let fjs = d.getElementsByTagName('script')[0];
let js = d.createElement('script');

js.id = id;
js.onload = callBack;
js.src = "https://www.eample.com/test.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'testId', scriptOnload));
</script>