[英]How can I access the DOM elements within an iFrame
我正在編寫一個jQuery插件,需要能夠在iFrame中針對DOM元素運行。 我現在正在本地測試這個(即url是file://.../example.html),在Chrome中我一直在點擊“SecurityError:無法從'HTMLIFrameElement'讀取'contentDocument'屬性:阻止了一個框架原點“null”來訪問跨域框架。“ 在Safari中我只得到一個空文檔。
鑒於父文件和iFrame的文件都來自我的本地磁盤(在開發中)並且將從相同的服務器(生產中)下來,我認為我不會受到跨源問題的影響。
有沒有辦法可以說服瀏覽器我的本地文件實際上是同一個域?
<旁邊> 有趣的是在Safari中,直接使用控制台,我可以輸入$("iframe").get(0).contentDocument.find("ol")
,它很高興找到我的列表。 在Chrome中,同一行會拋出安全錯誤,就像它正在執行一樣。 </一旁>
更新
基於下面的建議,我已經啟動了一個簡單的本地Web服務器來測試這個,現在我沒有得到跨源錯誤 - yay - 但我也沒有得到任何內容。
我的Javascript看起來像
$(document).ready(function(){
var myFrame = $("iframe"),
myDocument = $(myFrame.get(0).contentDocument),
myElements;
myDocument.ready(function(){
myElements = myDocument.find("ul, ol");
console.debug("success - iFrame", myFrame, "document", myDocument, "elements", myElements);
});
});
myDocument.ready
只是為了確保iFrame的文檔准備就緒 - 實際上它沒有任何區別。
我總是以myElements
為空。 ( []
in safari或Chrome中的jQuery.fn.init[0]
)
但是,如果我手動將其鍵入控制台:
$($("iframe").get(0).contentDocument).find("ol, ul")
我按預期得到了我的清單。 Safari和Chrome現在都是這種情況。
所以我的問題變成了:為什么我的腳本不能看到DOM元素,但是當直接輸入瀏覽器的控制台時,相同的代碼可以很高興地看到DOM元素?
Chrome具有默認安全限制,即使它們在技術上是相同的來源,也不允許您從硬盤訪問其他窗口。 Chrome中有一個標志可以放寬安全限制(Windows上的命令行參數是我記得的),但我不建議使用該標志運行以進行快速測試。 有關命令行參數的信息,請參閱此文章或本文 。
如果您從Web服務器(即使它是本地Web服務器)而不是您的硬盤驅動器運行文件,您將不會遇到此問題。
或者,您可以在其他不受限制的瀏覽器中進行測試。
現在您已將問題更改為不同的內容,您必須等待iframe窗口加載才能訪問其中的內容,並且您不能在不同的文檔上使用jQuery的.ready()
(它不會在另一份文件上工作)。
$(document).ready(function() {
// get the iframe in my documnet
var iframe = document.getElementById("testFrame");
// get the window associated with that iframe
var iWindow = iframe.contentWindow;
// wait for the window to load before accessing the content
iWindow.addEventListener("load", function() {
// get the document from the window
var doc = iframe.contentDocument || iframe.contentWindow.document;
// find the target in the iframe content
var target = doc.getElementById("target");
target.innerHTML = "Found It!";
});
});
測試頁面在這里 。
編輯:經過進一步的研究,我發現jQuery會像這樣為你做一些這樣的工作,jQuery解決方案似乎適用於所有主流瀏覽器:
$(document).ready(function() {
$("#testFrame").load(function() {
var doc = this.contentDocument || this.contentWindow.document;
var target = doc.getElementById("target");
target.innerHTML = "Found It!";
});
});
測試頁面在這里 。
在查看jQuery實現時,它真正做的就是在iFrame上設置一個load
事件監聽器。
如果您想了解調試/解決上述第一種方法的細節:
在我試圖解決這個問題時,我發現了一些非常奇怪的東西(在Chrome中)與iFrames。 當你第一次看到iframe的窗口時,有一個文件,它說它的readyState === "complete"
,你認為它已經完成加載,但它正在撒謊。 從URL加載到iframe的文檔中的實際文檔和實際<body>
標記實際上還沒有。 我通過在<body data-test="hello">
上放置一個自定義屬性並檢查該自定義屬性來證明這一點。 瞧,看哪。 即使document.readyState === "complete"
, <body>
標記上也沒有該自定義屬性。 因此,我總結(至少在Chrome中)iFrame最初有一個虛擬的空文檔和正文,這些文檔和正文一旦將URL加載到iFrame中,就不會是實際的文檔和正文。 這使得整個過程檢測到什么時候它已經准備好讓人感到困惑(這需要花費我幾個小時來解決這個問題)。 事實上,如果我設置一個間隔計時器並輪詢iWindow.document.body.getAttribute("data-test")
,我將看到它反復顯示為undefined
,然后最終它將顯示正確的值,所有這一切都與document.readyState === "complete"
,這意味着它完全撒謊。
我認為發生的事情是iFrame以虛擬和空文檔開始,然后在內容開始加載后替換。 另一方面,iFrame window
是真正的窗口。 因此,我發現實際等待加載內容的唯一方法是監視iFrame window
上的load
事件,因為這似乎並不存在。 如果您知道有一些您正在等待的特定內容,您也可以進行輪詢直到該內容可用。 但是,即使這樣你也要小心,因為你不能iframe.contentWindow.document
獲取iframe.contentWindow.document
因為如果你過早地獲取它將會是錯誤的文檔。 整件事情非常糟糕。 我找不到任何方法在iFrame文檔本身之外使用DOMContentLoaded
,因為您無法知道實際的document
對象是否到位,您可以將事件處理程序附加到它。 所以...我在iFrame窗口上安裝了load
事件,這似乎有效。
如果您實際控制了iFrame中的代碼,那么您可以更輕松地從iFrame本身觸發事件,方法是在iFrame代碼中使用帶有$(document).ready()
的jQuery和它自己的jQuery版本,或者通過調用在位於目標元素之后的腳本的父窗口中運行(從而確保目標元素已加載並准備就緒)。
進一步編輯
經過一系列的研究和測試之后,這里有一個函數可以告訴你iFrame何時遇到DOMContentLoaded
事件而不是等待load
事件(圖像和樣式表可能需要更長時間)。
// This function ONLY works for iFrames of the same origin as their parent
function iFrameReady(iFrame, fn) {
var timer;
var fired = false;
function ready() {
if (!fired) {
fired = true;
clearTimeout(timer);
fn.call(this);
}
}
function readyState() {
if (this.readyState === "complete") {
ready.call(this);
}
}
// cross platform event handler for compatibility with older IE versions
function addEvent(elem, event, fn) {
if (elem.addEventListener) {
return elem.addEventListener(event, fn);
} else {
return elem.attachEvent("on" + event, function () {
return fn.call(elem, window.event);
});
}
}
// use iFrame load as a backup - though the other events should occur first
addEvent(iFrame, "load", function () {
ready.call(iFrame.contentDocument || iFrame.contentWindow.document);
});
function checkLoaded() {
var doc = iFrame.contentDocument || iFrame.contentWindow.document;
// We can tell if there is a dummy document installed because the dummy document
// will have an URL that starts with "about:". The real document will not have that URL
if (doc.URL.indexOf("about:") !== 0) {
if (doc.readyState === "complete") {
ready.call(doc);
} else {
// set event listener for DOMContentLoaded on the new document
addEvent(doc, "DOMContentLoaded", ready);
addEvent(doc, "readystatechange", readyState);
}
} else {
// still same old original document, so keep looking for content or new document
timer = setTimeout(checkLoaded, 1);
}
}
checkLoaded();
}
這簡單地稱為:
// call this when you know the iFrame has been loaded
// in jQuery, you would put this in a $(document).ready()
iFrameReady(document.getElementById("testFrame"), function() {
var target = this.getElementById("target");
target.innerHTML = "Found It!";
});
鑒於父文件和iFrame的文件都來自我的本地磁盤(在開發中)並且將從相同的服務器(生產中)下來,我認為我不會受到跨源問題的影響。
“請打開我已附加到此電子郵件中的完全無害的 HTML文檔。” 瀏覽器有充分的理由將跨域安全性應用於本地文件。
有沒有辦法可以說服瀏覽器我的本地文件實際上是同一個域?
安裝Web服務器。 通過http://localhost
測試。 作為獎勵,獲得HTTP服務器的所有其他好處(例如能夠使用以/
開頭並使用服務器端代碼開發的相對URI)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.