JavaScript >> Javascript 文檔 >  >> Tags >> document

iframe、onload 和 document.domain

在互聯網已經成為的這個新的 Web 2.0 混搭世界中,很多焦點都放在了使用 iframe 將第三方內容嵌入到頁面上。 iframe 提供了一定程度的安全性,因為 JavaScript 訪問它受到域名的限制,因此包含來自另一個站點的內容的 iframe 無法訪問包含頁面上的 JavaScript。這種跨域限制是雙向的,因為包含頁面也沒有對 iframe 的編程訪問。在所有方面,包含頁面和 iframed 頁面都被切斷了通信(這導致了 HTML5 中的跨文檔消息傳遞 API)。在大多數圍繞 iframe 的討論中,缺少的部分是 JavaScript 對象所有權。

iframe 和所有權

iframe 元素本身,<iframe> , 由包含頁面所有,因此您可以將其作為一個元素進行處理(獲取/設置屬性、操作其樣式、在 DOM 中移動它等)。 window 表示 iframe 內容的對像是加載到 iframe 中的頁面的屬性。為了讓包含頁面以任何有意義的方式訪問 iframe 的 window 對象,包含頁面和 iframe 頁面的域需要相同(詳情)。

當域匹配時,包含頁面可以訪問 window iframe 的對象。 iframe 元素對像有一個名為 contentDocument 的屬性 包含 iframe 的 document 對象,因此您可以使用 parentWindow 檢索 window 的屬性 目的。這是檢索 iframe 的 window 的標準方法 對象並且被大多數瀏覽器支持。版本 8 之前的 Internet Explorer 不支持此屬性,因此您必須使用專有的 contentWindow 財產。示例:

function getIframeWindow(iframeElement){
    return iframeElement.contentWindow || iframeElement.contentDocument.parentWindow;
}

此外,可以使用 window.parent 從 iframe 中檢索包含頁面的窗口對象 財產。 iframe 頁面還可以使用 window.frameElement 檢索對其所在 iframe 元素的引用 財產。這跨越了所有權邊界,因為 iframe 由包含頁面擁有,但可以從 iframe 的 window 直接訪問 對象。

使用 iframe 元素的 onload

由於圍繞 iframe 的所有權問題,嘗試確定何時加載 iframe 是一項有趣的任務。不是 Internet Explorer 的瀏覽器做了一些非常有用的事情:它們暴露了一個 load iframe 元素的事件 這樣無論內容如何,您都可以知道 iframe 何時加載。由於 iframe 元素歸包含頁面所有,因此您無需擔心跨域限制。可以監視加載本地內容的 iframe 以及加載外部內容(實驗)的 iframe。示例代碼:

var iframe = document.createElement("iframe");
iframe.src = "simpleinner.htm";
iframe.onload = function(){
    alert("Iframe is now loaded.");
};
document.body.appendChild(iframe);

這適用於除 Internet Explorer(甚至是版本 8!)之外的所有瀏覽器。 我曾希望也許使用 attachEvent() 方法可行,可惜 Internet Explorer 不支持 load iframe 元素上的事件。 很失望。

使用 iframe 窗口的 onload

似乎 Internet Explorer 又要毀掉我的一天了。然後,我記得我並不擔心 iframe 中的外國內容。在我的具體情況下,我正在處理來自同一域的內容。由於沒有跨域限制,我可以訪問 iframe 的 window 直接對象並分配一個 onload 事件處理程序。示例:

var iframe = document.createElement("iframe"),
    iframeWindow;
iframe.src = "simpleinner.htm";
document.body.appendChild(iframe);
iframeWindow = iframe.contentWindow || iframe.contentDocument.parentWindow;
iframeWindow.onload = function(){
    alert("Local iframe is now loaded.");
};

這種方法的有趣之處在於您必須在 after 之後分配事件處理程序 iframe 元素已添加到頁面中。在此之前,iframe 的 window 對像不存在,因此您無法分配事件處理程序。此方法僅適用於相同域頁面的 Internet Explorer 和 Firefox。其他瀏覽器尚未創建 window 對象並因此引發錯誤(實驗)。

輸入document.domain

我已經放棄了使用一種方法來檢測 Internet Explorer 的 iframe 加載和其他所有瀏覽器的 iframe 加載,所以我繼續我的任務。接下來,我必須設置 document.domain 在包含頁面上,因為我有幾個不同的子域,我需要從中加載 iframe。使用不同子域時,設置document.domain 到主機名的根目錄允許 iframe 與其父級和彼此通信。例如,如果我必須從 www2.nczonline.net 加載 iframe 頁面 ,這在技術上被認為是一個不同的域,並且是不允許的。但是,如果我設置 document.domain 到包含頁面和 iframe 頁面中的“nczonline.net”,兩者都可以通信。只需一行代碼,最好放在頁面頂部:

document.domain = "nczonline.net";

這平衡了域差異,並允許一切都像兩個頁面來自同一個域一樣工作。或者我是這麼想的。

這種方法的問題在於,在加載 iframe 之前,它仍被視為由域擁有,具體在其 src 中 屬性。相對路徑會自動添加加載包含頁面的域 (www.nczonline.net ) 與分配給 document.domain 的那個相比 .這意味著 wnczonline.net 的比較 到 www.nczonline.net 當您嘗試訪問 iframe 的 window 時,同域檢查失敗並導致 JavaScript 錯誤 對象(實驗)。在加載 iframe 頁面並執行更改域的 JavaScript 命令之前,iframe 頁面不會更改其關聯域。但是,一旦加載了 iframe 頁面,一切正常。但是如何知道 iframe 頁面加載後呢?

反轉進程

由於還沒有找到確定 iframe 何時加載的跨瀏覽器解決方案,我決定扭轉我的想法。而不是包含頁面詢問何時加載 iframe,如果 iframe 告訴包含頁面它已加載怎麼辦?如果 iframe 頁面偵聽自己的 load 事件,然後告訴包含頁面何時發生,這應該可以解決問題。我希望這就像分配一個事件處理程序一樣簡單,所以我想出了以下想法:我將一個方法分配給 iframe 元素。然後,iframe 頁面將在加載時調用該方法。該方法必須分配給元素而不是 iframe 的 window 對象,因為後者在足夠早的時刻並不存在於所有瀏覽器中。結果是這樣的:

var iframe = document.createElement("iframe");
iframe.src = "simpleinner.htm";
iframe._myMethod = function(){
    alert("Local iframe is now loaded.");
};
document.body.appendChild(iframe);

此代碼分配了一個名為 _myMethod() 的方法 到 iframe 元素上。然後在 iframe 中加載的頁面添加以下代碼:

window.onload = function(){
    window.frameElement._myMethod();
}

由於這段代碼是在賦值給 document.domain 之後執行的 ,無需擔心安全限制。這適用於任何共享相同根主機名的資源(實驗)。它適用於所有瀏覽器,這正是我想要的,但是檢測 iframe 中何時加載外部資源的問題仍然讓我很煩。

使用 iframe 的 onreadystatechange

我決定多研究一下 Internet Explorer 的 iframe 實現。很明顯,將某些東西分配給 onload 屬性沒有產生預期的效果,但我想肯定還有其他類似的東西。我嘗試使用 attachEvent() 附加事件處理程序 ,這也不起作用。好的,顯然 iframe 上不支持 load 事件。其他的呢?

這時候我想起了 IE 奇怪的 readystatechange 它在文件上的事件。當然,這與 readystatechange 完全不同 在 XMLHttpRequest 上觸發的事件 對象。我想知道 iframe 元素是否也可能支持此事件,事實證明確實如此。 iframe 元素支持 readyState 屬性,當 iframe 的內容加載完畢後,該屬性變為“交互式”,然後“完成”。因為這是在 iframe 元素上,而不是在 iframe window 對象,不用擔心跨域限制(實驗)。我最終得到的代碼是這樣的:

var iframe = document.createElement("iframe");
iframe.src = "simpleinner.htm";

if (navigator.userAgent.indexOf("MSIE") > -1 && !window.opera){
    iframe.onreadystatechange = function(){
        if (iframe.readyState == "complete"){
            alert("Local iframe is now loaded.");
        }
    };
} else {
    iframe.onload = function(){
        alert("Local iframe is now loaded.");
    };
}

document.body.appendChild(iframe);

確定瀏覽器是否為 IE 的檢查有點混亂。我寧願檢查是否存在 iframe.readyState ,但是,當您在將 iframe 添加到文檔之前嘗試訪問該屬性時,這會引發錯誤。我考慮過使用 document.readyState 的存在 判斷是否使用readystatechange ,然而,大多數其他瀏覽器現在都支持這個屬性,所以這不是一個足夠好的決定因素。對於 YUI,我只使用 Y.UA.ie 確定這一點(您可以使用最適合您的方法)。

IE 的隱藏加載支持

發布此博客後不久,Christopher 評論說使用 attachEvent () 上的 iframe 元素在 IE 中有效。我本可以發誓我之前嘗試過這個,但由於他的提示,我開始了另一個實驗。事實證明,他是完全正確的。我不得不翻閱 MSDN 文檔,最終找到一個迂迴參考,但果然,它就在那裡。這導致了最終的代碼片段:

var iframe = document.createElement("iframe");
iframe.src = "simpleinner.htm";

if (iframe.attachEvent){
    iframe.attachEvent("onload", function(){
        alert("Local iframe is now loaded.");
    });
} else {
    iframe.onload = function(){
        alert("Local iframe is now loaded.");
    };
}

document.body.appendChild(iframe);

此代碼也適用於所有瀏覽器,並避免了與 readystatechange 的時間相關的任何潛在問題 事件與 load 事件。

總結

經過相當多的調查,似乎可以確定 iframe 何時在所有瀏覽器中加載,而不管 iframe 頁面的來源如何。這使得 iframed 內容的監控和錯誤處理更易於管理。我很感激所有瀏覽器供應商都看到了將這些事件添加到 iframe 元素本身而不是依賴於 iframe window 的好處 對像或期望我們通常不關心 iframe 是否已加載。

**更新(2009 年 9 月 15 日):**添加了關於 attachEvent() 的部分 基於克里斯托弗的評論。


Tutorial JavaScript 教程
  1. 可選鏈的成本。

  2. 引入 React 16.8,具有對 Hooks 的官方支持

  3. React:編寫自定義 API 掛鉤

  4. 使用 Express、Nodejs、Nodemailer 和 MailGun 提交表單並接收電子郵件 - 完整指南

  5. 如何在 Nuxt 中使路由區分大小寫

  6. 使用 NestJS 和 MongoDB (Mongoose) 構建 RESTful API

  7. 使用 Browserify + Babelify + Gulp 編譯和捆綁 Javascript es6

  1. 全棧公開 - 課程反思

  2. 集成 Python 和 JavaScript 的最佳方式?

  3. 2021 年掌握 JavaScript 的 5 個項目

  4. 如何使用 Vue 構建 Chrome 擴展

  5. 學習 Svelte 第 5 部分

  6. 在 bitbucket 管道上運行“react-scripts build”時運行我們的內存

  7. 如何從javascript中的字符串中刪除

  1. 在 30 分鐘內使用 Svelte 構建響應式網站。

  2. 使用 Clean Architecture 在前端遠離 ReactJs 和 VueJs

  3. 第 1 部分:使用 Vite、Vue 3、Quasar 和 Pinia 統一 SVG 圖標

  4. 宣布 Ionic React 候選版本!