Chrome 具有默认的安全限制,不允许您从硬盘访问其他窗口,即使它们在技术上是同源的。Chrome 中有一个标志可以放宽该安全限制(我记得 Windows 上的命令行参数是这样),但我不建议使用该标志运行超过快速测试。有关命令行参数的信息,请参阅这篇文章或这篇文章。
如果您在 Web 服务器(即使它是本地 Web 服务器)而不是硬盘驱动器上运行文件,则不会出现此问题。
或者,您可以在其他不那么严格的浏览器中进行测试。
现在您已将问题更改为不同的内容,您必须等待 iframe 窗口加载,然后才能访问其中的内容,并且您不能.ready()
在其他文档上使用 jQuery (它不适用于其他文档) )。
$(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 实现时,它真正做的就是load
在 iFrame 本身上设置一个事件侦听器。
如果您想了解调试/解决上述第一种方法的具体细节:
在我试图解决这个问题的过程中,我发现了一些非常奇怪的东西(在 Chrome 中)与 iFrames。当您第一次查看 iframe 的窗口时,有一个文档,它说readyState === "complete"
您认为它已完成加载,但它在撒谎。<body>
通过 URL 加载到 iframe 的文档中的实际文档和实际标签实际上还不存在。我通过在 上放置自定义属性<body data-test="hello">
并检查该自定义属性来证明这一点。瞧,瞧。即使document.readyState === "complete"
该自定义属性不在<body>
标签。因此,我得出结论(至少在 Chrome 中)iFrame 最初有一个虚拟的空文档和正文,这些文档和正文并不是在将 URL 加载到 iFrame 后将就位的实际文档。这使得检测何时准备就绪的整个过程变得非常混乱(我花了好几个小时来弄清楚这一点)。事实上,如果我设置一个间隔计时器和 poll iWindow.document.body.getAttribute("data-test")
,我会看到它undefined
重复显示,然后最后它会显示正确的值,所有document.readyState === "complete"
这些都意味着它完全在撒谎。
我认为正在发生的事情是 iFrame 以一个虚拟的空文档和正文开始,然后在内容开始加载后替换它。另一方面,iFramewindow
是真正的窗口。因此,我发现实际等待加载内容的唯一方法是监视load
iFrame 上的事件,window
因为这似乎并没有说谎。如果您知道有一些特定的内容正在等待,您也可以轮询直到该内容可用。但是,即便如此,您也必须小心,因为您不能iframe.contentWindow.document
过早获取该文件,因为如果您过早获取它,它将是错误的文档。整个事情都非常破碎。我找不到任何DOMContentLoaded
从 iFrame 文档本身外部使用的方法,因为您无法知道实际的document
对象就位,您可以将事件处理程序附加到它。所以......我load
在 iFrame 窗口上解决了这个事件,它似乎确实有效。
如果您实际上控制 iFrame 中的代码,那么您可以更轻松地从 iFrame 本身触发事件,方法是$(document).ready()
在 iFrame 代码中使用 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!";
});