很久前討論過ready()與load()的差異,今天處理IFrame load()事件時,又有新發現。

依jQuery文件:

The load event is sent to an element when it and all sub-elements have been completely loaded. This event can be sent to any element associated with a URL: images, scripts, frames, iframes, and the window object.

需求是要在IFrame載入後由內嵌網頁中的元素取出資訊,透過.load()事件是最有效的切入點。但因為程式架構的關係,$("#myIFrame").load(function() { … });會在IFrame已載入後才執行,我發現在這種情境下,load事件將不會觸發。底下用程式來模擬:

先寫一個透過delay參數控制是否延遲兩秒才回傳的ASP.NET網頁(FramePage.aspx)以便控制內嵌網頁載入完成的時機:

<%@ Page Language="C#" %>
 
<!DOCTYPE html">
 
<script runat="server">
    void Page_Load(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(Request["delay"]))
        {
            System.Threading.Thread.Sleep(2000);
            t.InnerHtml = "Delayed FramePage";
        }
        else t.InnerHtml = "FramePage";
    }
</script>
 
<html>
<head runat="server">
    <title></title>
</head>
<body>
<div id='t' runat="server" 
     style='width: 400px; height: 1200px; background-color: Silver;'>
</div>
</body>
</html>

接著,建立一個網頁放入兩個IFrame,src分別指向FramePage.aspx及FramePage.aspx?delay=Y(後者會延遲兩秒才載入完成)。網頁一開始就對兩個IFrame加掛load事件,透過setTimeout做法於1秒後再對兩個IFrame掛上第二組load事件。load事件中透過contents().find("#t")存取IFrame內的DOM元素(注意: 若IFrame內嵌網頁來自其他網站,則會因Same-Origin Policy限制無法存取,參考)

<!DOCTYPE html>
 
<html>
<head>
    <title>Load Event Test</title>
    <style>
        .banner  
        {
            padding: 5px; margin-bottom: 5px; width: 400px;
            background-color: Brown; color: White; 
         }
    </style>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.4.js"> 
    </script>
    <script>
        $(function () {
            var $log = $("#log");
            function showMsg(m) {
                $log.append("<li>" + m + "</li>");
            }
            var $frms = $("#frmA,#frmB");
            $frms.load(function () {
                showMsg("Load Event for " + this.id + "->" + 
                    $(this).contents().find("div").text());
            });
            setTimeout(function () {
                $frms.load(function () {
                    showMsg("Load Event(setTimeout) for " + this.id + "->" +
                    $(this).contents().find("div").text());
                });
            }, 1000);
        });
    </script>
</head>
<body>
<div class="banner">Banner</div>
<iframe id='frmA' src="FramePage.aspx" style='width: 200px; height: 100px;' 
 frameborder="0" border="0" cellspacing="0"></iframe>
<iframe id='frmB' src="FramePage.aspx?delay=Y" style='width: 200px; height: 100px;' 
 frameborder="0" border="0" cellspacing="0"></iframe>
<ul id="log"></ul>
</body>
</html>

執行結果如下,FramePage(frmA)是一開始就載入的,而Delayed FramePage(frmB)則要等2秒才會載入完成,發現透過setTimeout等1秒才設定的load事件,會因frmA已載入完成而未被觸發,2秒後才載入完成的frmB則二個事件都被觸發。(如下圖)

最後,我試著透過改掛ready事件,並加上setInterval檢查元件狀態的技巧解決問題。(另外,還發現在ready事件中,this不等於觸發事件的對象,需另外設法取得)

            var $frms = $("#frmA,#frmB");
            $frms.load(function () {
                showMsg("Load Event for " + this.id + "->" +
                    $(this).contents().find("div").text());
            });
            setTimeout(function () {
                $frms.each(function () {
                    //在ready事件中,this不會指向IFrame,故這裡另設變數保存之
                    var frm = this;
                    $(frm).ready(function () {
                        //ready事件不保證DOM已載入完成,故可能取不到資料
                        showMsg("Ready Event for " + frm.id + " -> " +
                                $(frm).contents().find("div").text());
                        //利用setInterval 0.1秒觸發一次檢查
                        var hnd = setInterval(function () {
                            var t = $(frm).contents().find("div").text();
                            //若已取到資料
                                 if (t) {
                                //顯示結果
                                     showMsg("Ready + setInterval Trick for " +
                                frm.id + " -> " + t);
                                //終止setInterval輪詢檢查
       clearInterval(hnd);
                            }
                        }, 100);
                    });
                });
            }, 1000);

測試結果如下。ready事件一開始可取得已載入完成的frmA內容,但無法取得frmB的內容(因尚未載入),最後透過setInterval輪詢檢查技巧,總算能涵蓋frmA及frmB的情境。


Comments

Be the first to post a comment

Post a comment