Chrome 具有默认的安全限制,不允许您从硬盘访问其他窗口,即使它们在技术上是相同的来源。 Chrome 中有一个标志可以放宽该安全限制(我记得 Windows 上的命令行参数),但我不建议使用该标志运行超过快速测试。有关命令行参数的信息,请参阅 this post 或 this 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 事件侦听器。
如果您想了解调试/解决上述第一种方法的细节:
在我试图解决这个问题的过程中,我发现了一些非常奇怪的东西(在 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 文档本身之外找到使用 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!";
});