如何動態加入包含onload回呼函式的script元素

說明

在下面的html範例中,包含了兩個frame,分別為headerFramecontentFrame。 然而,我想要在這份html管理contentFrame的一些js的全域變數。 可是,我如果放在這份html文件的head裡面的話,frame裡面完全抓不到這些全域變數。 除此之外,如果我要在這些檔案被載入的時候,執行一些自訂的函式又要怎麼做?

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE>
<html>
<head>
...
</head>
<body>
<frameset>
<frame name="headerFrame"></frame>
<frame name="contentFrame"></frame>
</frameset>
</body>
</html>

作法

一開始,我很單純地以為用document.createElement('script')就好,沒想到卻沒這麼簡單。 我一共遇到了3個問題:

  • 怎麼樣取得contentFrame裡頭的document?
  • 怎樣把建立好了script加到該document裡面?
  • 如何綁定onload的回呼函式,且支援IE7?

Step 1

要怎麼取得frame裡面的document物件? 先在window物件的frames屬性把contentFrame取出來,再將這個framedocument取出來。

1
var frameDoc = window.frames['contentFrame'].document;

Step 2

接著,怎麼樣把script加到document? 先用document.createElement()創建一個新的script元素,並設定這個元素的srctype屬性。 然後用frameDoc物件(contentFrame裡面的document)的head屬性提供的appendChild()方法將元素加到head裡面去。

1
2
3
4
5
var frameDoc = window.frames['contentFrame'].document,
newScript = document.createElement('script');
newScript.src = '/js/boswer.min.js';
newScript.type = 'text/javascript';
frameDoc.head.appendChild(newScript);

這邊有另外一個小問題,document.head這個屬性在IE 9以後才開始支援。 所以要做個簡單的填充工具,利用document.getElementsByTagName()head加到document中。

1
2
3
4
5
6
var frameDoc = window.frames['contentFrame'].document,
newScript = document.createElement('script');
newScript.src = '/js/boswer.min.js';
newScript.type = 'text/javascript';
if(!frameDoc.head) frameDoc.head = frameDoc.getElementsByTagName('head')[0];
frameDoc.head.appendChild(newScript);

Step 3

一般來講,onload會是script元素的一個屬性,只需把function指派給這個物件即可。

1
2
3
4
5
6
7
8
9
10
11
var frameDoc = window.frames['contentFrame'].document,
newScript = document.createElement('script');
newScript.src = '/js/boswer.min.js';
newScript.type = 'text/javascript';
newScript.onload = _onload;
if(!frameDoc.head) frameDoc.head = frameDoc.getElementsByTagName('head')[0];
frameDoc.head.appendChild(newScript);

function _onload() {
// script被載入之後才會執行的程式碼
}

問題又來了,IE 9之後才開始支援DOMContentLoaded。 因此,可以採用script.onreadystatechange()作為舊版本IE的替代方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var frameDoc = window.frames['contentFrame'].document,
newScript = document.createElement('script');
newScript.src = '/js/boswer.min.js';
newScript.type = 'text/javascript';
newScript.onload = _onload;
newScript.onreadystatechange = function () {
if (this.readyState == 'complete') _onload();
}
if(!frameDoc.head) frameDoc.head = frameDoc.getElementsByTagName('head')[0];
frameDoc.head.appendChild(newScript);

function _onload() {
// script被載入之後才會執行的程式碼
}

參考資料

document.head - caniuse.com
onload - caniuse.com
Object.onload in Internet Explorer 6, 7 and 8 - StackOverflow