iframe 動態onload事件處理方式


轉自:http://w3help.org/zh-cn/causes/SD9022

 

標准參考

關於 HTML 4.01 規范中 BODY 標記的 onload 屬性說明: http://www.w3.org/TR/html401/struct/global.html#h-7.5.1

關於 HTML 4.01 規范中 onload 內在事件說明:http://www.w3.org/TR/html401/interact/scripts.html#adef-onload

關於 DOM Level2 Events 規范中 load 事件說明:http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-htmlevents

問題描述

頁面加載完成后會觸發 onload 事件,通常下會使用 window.onload 、 document.body.onload、 HTMLIFrame.onload 方法來處理他;但是各瀏覽器對頁面 onload 事件處理方式並不一致,這些方法可能會導致頁面加載完成后無法觸事件處理函數。

造成的影響

window.onload 與 document.body.onload 事件相互關系不明確。

IE6 IE7 IE8 中無法通過為屬性賦值方式為 IFRAME 元素綁定 load 事件處理函數。

受影響的瀏覽器

IE6 IE7 IE8 Firefox  

問題分析

一、window.onload 與 document.body.onload 事件以及 BODY 標簽內的內聯 onload 事件,三者相互關系不明確

有關這兩個事件在 W3C 指定的規范中並沒有說明其區別,由於 window 對象屬於 BOM 范疇沒有統一規范可依,各瀏覽器對其在此處的實現存有差異。

在微軟的 MSDN 中說明 document.body.onload 屬性是 window 的 onload 事件處理程序。其它瀏覽器廠商並沒有對此屬性做更細致解釋。

分析以下代碼:

<button onclick="alert(document.body.onload);">查看本頁 document.body.onload 事件</button>
<button onclick="alert(window.onload);">查看本頁 window.onload 事件</button>
<button onclick="alert(window.onload===document.body.onload);">查看document.body.onload 事件與 window.onload 事件是否為同一事件</button>
<br />
<script>
document.body.onload = function (){ // 函數 A
  document.getElementById("A").innerHTML = "document.body.onload 被觸發";
}
window.onload = function (){ // 函數 B document.getElementById("B").innerHTML = "window.onload 被觸發"; } </script> <span id="A" style="background:gold"></span> <span id="B" style="background:gray"></span> 

代碼中分別對兩種 onload 屬性賦值了不同的事件處理函數。其中 window.onload 先於 document.body.onload 被定義。如果他們兩者使用的是同一事件處理源,則會發生 window 對象中 onload 事件定義被 body 的事件處理函數所覆蓋。

實際上個瀏覽器運行結果如下:

  IE6 IE7 IE8 Firefox Chrome Safari Opera
實際運行 window.onload 被觸發
document.body.onload 事件函數 函數 B 函數 A 函數 B
window.onload 事件函數 null 函數 B
兩者事件處理函數是否一致 Flase Flase true

初步可以看出:

  • 在 IE 中 window.onload 事件函數即使定義了,也會是 null ,它的事件處理函數始終是為 document.body.onload 提供的;
  • 在 Firefox 中 document.body.onload 事件不與 window.onload 事件相同,后者事件函數不會覆蓋前者;
  • 在 Chrome Safari Opera 中 document.body.onload 事件處理函數被后聲明的 window.onload 事件處理函數覆蓋。

為了更明確以上猜測,我們將兩個事件處理函數位置對調后再來觀察實際效果:

window.onload = function () // 函數 B{
  document.getElementById("B").innerHTML = "window.onload 被觸發";
}

document.body.onload = function (){ // 函數 A document.getElementById("A").innerHTML = "document.body.onload 被觸發"; } 

這次試圖使用 document.body.onload 事件處理函數來覆蓋 window.onload,實際運行結果如下:

  IE6 IE7 IE8 Firefox Chrome Safari Opera
實際運行 document.body.onload 被觸發 window.onload .onload 被觸發 document.body.onload 被觸發
document.body.onload 事件函數 函數 A 函數 A 函數 A
window.onload 事件函數 null 函數 B
兩者事件處理函數是否一致 Flase Flase true

可以看出:

  • 無論如何在 IE 中 window.onload 事件函數即使定義了,也會是 null ,它的事件處理函數始終是為 document.body.onload 提供的。
  • 在 Firefox 中 只有 window.onload 事件會觸發頁面加載完成事件,document.body.onload 只是個由用戶擴展的方法,並不觸發相應事件處理函數。這也說明了為什么僅在 Firefox 瀏覽器中,兩者"事件"函數均被保留。
  • 在 Chrome Safari Opera 中頁面加載完成事件由 document.body.onload 和 window.onload 兩者的事件處理函數定義順序有關,后定義的覆蓋之前定義的,兩者最終會使用同一事件源函數處理事件內容。

在實際應用中 onload 事件還可能被寫在 BODY 標簽的屬性內,而不在腳本標簽中定義他的處理函數。此時代碼如下:

<script>
window.onload=function(){
  alert("window onload");
}
</script>
<body onload="alert('overridden window onload')">

此用例中首先在腳本標簽內為 window.onload 事件附加了處理函數,然后有在 BODY 標簽內寫入 onload 事件處理代碼。如果 BODY 標簽內的 onload 事件處理與 window.onload 事件處理不同,就會彈出兩次對話框,並且可以根據對話框出現的順序判斷哪個 onload 事件處理在前。反之則只會彈出一個對話框,如果僅出現 "window onload" 字樣提示就說明 BODY 標簽內 onload 屬性無效,如果出現 “overridden window onload” 提示就說明 BODY 標簽內 onload 事件與 window.onload 事件為同一事件處理機制,后者覆蓋了前者。

各瀏覽器運行結果如下:

  IE6 IE7 IE8 Firefox Chrome Safari Opera
實際運行 ”overridden window onload“
兩者均代表 window onload 事件 true

可見,在所有瀏覽器中均將,BODY 標簽內的內聯 onload 事件處理等同與 window.onload 事件處理。

結合上文說明可知, Firefox 中 BODY 標簽內的 onload 事件屬性與腳本標記內寫入的 document.body.onload 事件並不一致。

 

二、IE6 IE7 IE8 中無法通過為屬性賦值方式為 IFRAME 元素綁定 load 事件處理函數

某些情況下可能需要為 IFRAME 標記動態添加 onload 監聽事件,其實現方法有通常有三種:

  1. 直接為 HTMLIFrameElement.onload 屬性為 IFRAME 標記賦值事件處理函數
  2. 使用 HTMLIFrameElement.setAttribute 方法為 IFRAME 標記賦值事件處理函數
  3. 使用事件監聽方式為 IFRAME 的 onload 事件綁定處理函數

根據 DOM 規范推薦使用第三種處理方式,但是由於規范在不斷修正添加中,瀏覽器開發時可能相應的標准規范並未出現或者並不完善,這導致了各個版本瀏覽對此問題的實現差異。

以下將依次分析這三種方式在各瀏覽器內的處理情況。

 

1.直接為 HTMLIFrameElement.onload 屬性為 IFRAME 標記賦值事件處理函數

分析以下代碼:

<body>
<div id="msgA">通過 HTMLIFrameElement.onload 屬性動態加入事件處理函數的 IFRAME 沒有觸發 onload 事件。</div>
<div id="msgB">靜態 IFRAME 沒有觸發 onload 事件。</div>
<br />
<script>

function iframeLoadA(){
  document.getElementById("msgA").innerHTML = "通過 HTMLIFrameElement.onload 屬性動態加入事件處理函數的 IFRAME 已經觸發 onload 事件。";
}
function iframeLoadB(){
  document.getElementById("msgB").innerHTML = "靜態 IFRAME 已經觸發 onload 事件。";
}

window.onload = function (){
  var iframeB = document.getElementById("ifarmeB");
  var iframeA = document.createElement('iframe');
  iframeA.onload = iframeLoadA;
  document.body.appendChild(iframeA);
}

</script>
<iframe id="ifarmeB" onload="iframeLoadB()"></iframe>
</body>

頁面中存在兩個 IFRAME 標記,ifrmaeA 標記為動態生成,並且動態為其 onload 屬性賦值了加載完成后的處理函數。而 iframeB 標記則是在頁面中靜態存在的,其 onload 屬性對應的事件處理函數已經被固定寫入。

運行此代碼,在各個瀏覽器中效果如下:

  IE6 IE7 IE8 Firefox Chrome Safari Opera
HTMLIFrameElement.onload = Function 沒有觸發 onload 事件 觸發 onload 事件
靜態 onload 屬性定義 觸發 onload 事件

可見,在 IE 瀏覽器中通過 HTMLIFrameElement.onload 屬性賦值方式無法響應 IFRAME 標記的 onload 事件。而其他瀏覽器均支持此寫法。

 

2.使用 HTMLIFrameElement.setAttribute 方法為 IFRAME 標記賦值事件處理函數

將以上代碼稍作修改,將 HTMLIFrameElement.onload 賦值語句部分修改為 HTMLIFrameElement.setAttribute方法:

window.onload = function (){
  var iframeB = document.getElementById("ifarmeB");
  var iframeA = document.createElement('iframe');
  iframeA.setAttribute("onload","iframeLoadA()");
  document.body.appendChild(iframeA);
}

此時 ifrmaeA 標記的 onload 屬性以及事件處理函數被 DOM 標准方法 setAttribute 寫入。

運行此代碼,在各個瀏覽器中效果如下:

  IE6 IE7 IE8(Q) IE8(S) Firefox Chrome Safari Opera
HTMLIFrameElement.setAttribute 沒有觸發 onload 事件 觸發 onload 事件
靜態 onload 屬性定義 觸發 onload 事件

可見,在 IE 系列瀏覽器中僅有 IE8(S) 支持通過 setAttribute 方法為 IFRAME 標記寫入 onload 屬性以及其事件處理函數。而其他瀏覽器均支持此寫法。

 

3.使用事件監聽方式為 IFRAME 的 onload 事件綁定處理函數

再將以上代碼稍作修改,使用 DOM 規范中推薦的事件監聽綁定方法為 IFRAME 動態加入 onload 事件處理函數:

function addEvent(eventName,element,fn){
  if (element.attachEvent) element.attachEvent("on"+eventName,fn);
  else element.addEventListener(eventName,fn,false);
}
window.onload = function (){
  var iframeB = document.getElementById("ifarmeB");
  var iframeA = document.createElement('iframe');
  addEvent("load",iframeA,iframeLoadA);
  document.body.appendChild(iframeA);
}

代碼中為了兼容 IE 私有的事件綁定方式,封裝了名為 addEvent 的處理函數,他為 IE 和其他瀏覽器提供不同的事件綁定方法,以達到兼容效果。

運行此代碼,在各個瀏覽器中效果如下:

  IE6 IE7 IE8(Q) IE8(S) Firefox Chrome Safari Opera
onload 事件監聽綁定 觸發 onload 事件
靜態 onload 屬性定義

此時所有瀏覽器中為 IFRAME 標記動態綁定的 onload 事件處理方法均生效。

 

綜合以上三種常見情況可以得出結論,除 IE 以外的瀏覽器均支持對以上三種動態定義 onload 事件處理函數的方法,而所有 IE 系列瀏覽器對 HTMLIFrameElement.onload 屬性的賦值不被支持,他們可以通過 DOM 標准方法 setAttribute 來為 HTMLIFrameElement 類型元素設置 onload 屬性,但是由於 IE8 以下版本對 setAttribute 方法支持有限,導致僅 IE8(S) 下生效。最終 IE 瀏覽器只能通過事件監聽方式為 HTMLIFrameElement 類型元素綁定 onload 事件處理函數。

 

解決方案

  1. 統一為 window 對象的 onload 事件綁定函數,避免在 Firefox 中產生 document.body.onload 事件理解歧義。
  2. 統一使用 DOM 規范的事件監聽方法(或 IE 專有事件綁定方法)為 IFRAME 標記綁定 onload 事件處理函數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM