前言
經常會遇到這樣一種情況。
在iframe里嵌入另外一個頁面時。如果iframe載入的頁面響應較快,或許我們感覺不到頁面載入的不同步,但試想,如果一個需要內嵌到iframe里的頁面的響應很慢,這里會出現一種什么現象呢?這時將會出現所有頁面已經載入完成,但
iframe元素處,將會出現空白,直到內嵌頁面完成載入時,該空白處才會顯示新載入的頁面。可想而知,一個頁面如果長時間的空白,對於瀏覽者來說將意味着什么。如果在內嵌頁面未載入完成時,給出一種加載提示信息。如:“頁面加載
中”之類的,我想這對瀏覽頁面用戶來講,將不再是煎熬,更是一種視覺上的享受。
為了實現這樣的效果,一般會采用如下原理處理。
-
iframe載入區域給出友好的提示信息。
-
當iframe載入完成時,清空提示信息,而讓iframe顯示。
這些都比較容易,但現在的問題的關鍵是怎么監聽iframe元素內的頁面已經載入完成。
關鍵這個問題,一般來講,會分兩種情況來討論解決方案。
-
同域嵌套:最好是讓子頁面調用父頁面的方法。
-
異域嵌套:如果是異域,但子頁面無法修改,那么:在Firefox/Opera/Safari中,可以直接使用iframe onload事件;而在IE中,可以通過定時器測定子頁面的document.readyState,或者使用iframe onreadystatechange事件計算該事件的響應。
一個簡單的包含iframe的父頁面將是如下樣子的:
<!DOCTYPE ...>
<html xmlns="...">
<head>
<meta ... />
<title>Iframe</title>
<body>
<iframe name="iframe1" id="iframe1" width="300" height="50" src="#" ></iframe>
<script type="text/javascript">//codes here</script>
</body>
</html>
開始:
一、同域嵌套
調用父頁面對象
parent.html function ifrmLoaded() { // code here
} sub.html window.onload = function() { window.parent.ifrmLoaded(); }
缺點:
-
子父頁面必須在同域中
- 需對子頁面有修改權;或者,可以請負責此子頁面的同事為我們添加一段代碼
<script type=”text/javascript”>
if(window.parent!=window) window.parent.iframeCall(); </script>
把它放到window.onlad中,或者直接放在</body>之前。
注[1]:在對iframe或其它窗口性質的前端編程中,同域名是最完美的先天條件。只要在同一域名中,各個窗口間的對象是共享的,我們完全可以自由發揮,在不同的窗口間來回駕馭。總之,只有想不到,沒有做不到。
有時候,為了防止自己的頁面不被別人嵌套,可以采用如下方式解決:
if(window.parent!=window) window.parent.location="http://hqlong.com"; //or
if(window.top!=window) window.top.location="http://hqlong.com";
二、異域嵌套(或者子頁面已存在且無法修改)
在不同域名的頁面,瀏覽器出於安全考慮,幾乎完全封鎖了頁面間的對象來往
在異域的頁面嵌套中,子頁面總是可以直接改變父窗口的location以防止被嵌套,但父頁面對這個一點辦法也沒有。當然,子頁面除了僅僅永恆地擁有父窗口.location的修改權外,也沒有其它了。例如,在IE下,子頁面只能直接修改父頁面
的.location為另一個源:
<script tyle=”text/javascript”>parent.location=”http://anotherPage.com/”;</script>
但無法訪問其它對象,如window.name,document等,連location.href等location的子屬性就無法訪問。當然,在防止嵌套方面,使用top.location會更強大。
方法一:使用onreadystatechange來判斷
【Firefox/Opera/Safari中直接使用iframe onload事件】
document.getElementById('ifrm').onload = function() { //here doc
}
【在IE下,注冊iframe onreadystatechange事件】
iframe onreadystatechange事件
var oFrm = document.getElementById('ifrm'); oFrm.onreadystatechange = function() { if (this.readyState && this.readyState == 'complete') { onComplete(); } }
定時器測document.readyState
var oFrm = document.getElementById('ifrm'); var fmState=function(){ var state=null; if(document.readyState){ try{ state=oFrm.document.readyState; }catch(e){state=null;} if(state=="complete" || !state) { onComplete(); return; } window.setTimeout(fmState,10); } }; //在改變src或者通過form target提交表單時,執行語句:
if(fmState.TimeoutInt) window.clearTimeout(fmState.timeoutInt); fmState.timeoutInt = window.setTimeout(fmState,400);
每當iframe加載頁面,過程內會激活onreadystatechange事件三次,相應的狀態分別是loading,interactive和complete,而最后一次才是complete.
問題一:為什么要延時400毫秒?
因為javascript對DOM的操作是異步的,我們必須等待腳本對DOM落實執行后才開始下一步。400秒這個數取決 與客戶端的設備和瀏覽器的響應速度,好的設備的響應速度能在10毫秒以內甚至更快,但100毫秒左右可能比較大眾化,400毫秒應該是十分保守的了。總 之,較大的毫秒數能適合更多的用戶設備狀況,並能減少了客戶端設備的工作量。至於document.readyState,指的是iframe內子頁的docuent.readyState,而不是父頁面的
問題二:為什么使用try和 catch?
因為在異域的情況下,當iframe的子頁到達interactive狀態時,父頁面就會失去訪問權,所以最多只能返回到loaded這一步,因此IE出一個未知錯誤——其實就是沒有權限,所以try和catch,讓這個錯誤沉默下去。
【兼容Firefox/Opera/Safari/IE的處理方式】
var oFrm = document.getElementById('ifrm'); oFrm.onload = oFrm.onreadystatechange = function() { if (this.readyState && this.readyState != 'complete') return; else { onComplete(); } }
或者
var $iFrame=$("#IFrame"); $iFrame.prop("src","http://www.baidu.com"); if (!/*@aijquery@*/0) { //如果不是IE,IE的條件注釋
$iFrame[0].onload = function(){ alert("加載完畢"); }; }else{ $iFrame[0].onreadystatechange = function(){ // IE下的節點都有onreadystatechange這個事件
if (iframe.readyState == "complete"){ alert("加載完畢"); } }; }
方法二:使用attachEvent判斷
var $iFrame=$("#IFrame"); $iFrame.prop("src","http://www.360.cn"); if ($iFrame[0].attachEvent){ $iFrame[0].attachEvent("onload", function(){ // IE
alert("加載完畢"); }); } else { $iFrame[0].onload = function(){ // 非IE
alert("加載完畢"); }; }
方法三:使用jquery里的load來判斷
var $iFrame=$("#IFrame");
$iFrame.prop("src","http://www.aijquery.cn");
$iFrame.load(function(){
alert("加載完畢");
});
注意
上面的方法必須是動態創建iframe或者動態添加iframe指定的src地址的情況下使用