gpt4 book ai didi

javascript - 如何在iFrame中访问DOM元素

转载 作者:行者123 更新时间:2023-12-03 12:40:00 26 4
gpt4 key购买 nike

我正在编写一个jQuery插件,该插件必须能够在iFrame中针对DOM元素运行。我现在只是在本地进行测试(即url为file://.../example.html),在Chrome中,我一直打“SecurityError:无法从'HTMLIFrameElement'中读取'contentDocument'属性:阻止了框架通过访问跨域框架而原点为“null”。”在Safari中,我只得到一个空文档。

鉴于父文件和iFrame的文件都来自本地磁盘(开发中)并且将从同一台服务器(生产中)脱离,我以为我不会受到跨域问题的影响。

有什么办法可以使浏览器确信我的本地文件实际上是同一域的?



更新

根据下面的建议,我已经启动了一个简单的本地Web服务器来对此进行测试,现在没有收到跨域错误-是的-但我也没有得到任何内容。

我的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为空结束。 (Safari中的 []或Chrome中的 jQuery.fn.init[0])

但是,如果我在控制台中手动输入以下内容:
$($("iframe").get(0).contentDocument).find("ol, ul")

我得到了预期的 list 。现在在Safari和Chrome中都是如此。

所以我的问题变成了:为什么我的脚本看不到DOM元素,但是直接输入到浏览器控制台中的相同代码可以愉快地看到DOM元素?

最佳答案

Chrome具有默认的安全限制,即使从技术上讲它们是相同的,也不允许您从硬盘访问其他窗口。 Chrome中有一个标志可以放宽该安全限制(我记得Windows上的命令行参数),尽管我不建议使用该标志来进行快速测试。有关命令行参数的信息,请参见this postthis article

如果您是在网络服务器(即使是本地网络服务器)而不是硬盘驱动器上运行文件,则不会出现此问题。

或者,您可以在非限制性的其他浏览器中进行测试。

现在,您已经将问题更改为其他问题,您必须等待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!";
});
});

测试页面 here

编辑:经过进一步研究,我发现jQuery会像这样为您完成其中的一些工作,并且jQuery解决方案似乎可以在所有主要浏览器中使用:
$(document).ready(function() {
$("#testFrame").load(function() {
var doc = this.contentDocument || this.contentWindow.document;
var target = doc.getElementById("target");
target.innerHTML = "Found It!";
});
});

测试页面 here

在查看jQuery实现时,实际上要做的就是在iFrame本身上设置 load事件监听器。

如果您想了解调试/解决上述第一种方法的细节,请执行以下操作:

在尝试解决此问题的过程中,我发现了iFrames(在Chrome中)非常奇怪的东西。当您第一次查看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 load上的 window事件,因为这似乎不是在说谎。如果您知道有一些特定的内容正在等待,则还可以轮询直到该内容可用。但是,即使那样,您也必须小心,因为您不能过早获取 iframe.contentWindow.document,因为如果过早获取它将是错误的文档。整个事情都坏了。我无法从iFrame文档本身之外找到使用 DOMContentLoaded的任何方法,因为您无法知道实际的 document对象是否存在,您可以将事件处理程序附加到该对象上。所以...我在iFrame窗口上解决了 load事件,该事件似乎确实起作用。

如果您实际上控制了iFrame中的代码,则可以通过iFrame本身使用的jQuery版本在iFrame代码中使用带有 $(document).ready()的jQuery或通过从iFrame调用父窗口中的函数来从iFrame本身更轻松地触发事件。脚本位于目标元素之后(从而确保目标元素已加载并准备就绪)。

进一步编辑

经过大量的研究和测试之后,下面的函数可以告诉您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!";
});

关于javascript - 如何在iFrame中访问DOM元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24603580/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com