尝试访问以编程方式创建的 <iframe> 的文档对象时出现“访问被拒绝”JavaScript 错误(仅限 IE)

IT技术 javascript internet-explorer dom iframe document
2021-01-19 13:03:30

我有一个项目,我需要在其中使用 JavaScript 创建一个 <iframe> 元素并将其附加到 DOM。之后,我需要在 <iframe> 中插入一些内容。这是一个将嵌入第三方网站的小部件。

我没有设置 <iframe> 的“src”属性,因为我不想加载页面;相反,它用于隔离/沙箱我插入的内容,这样我就不会遇到与父页面的 CSS 或 JavaScript 冲突。我正在使用 JSONP 从服务器加载一些 HTML 内容并将其插入到这个 <iframe> 中。

我有这个工作正常,有一个严重的例外 - 如果在父页面中设置了 document.domain 属性(它可能在部署此小部件的某些环境中),Internet Explorer(可能是所有版本,但我已经在 6、7 和 8 中确认)当我尝试访问我创建的这个 <iframe> 的文档对象时,出现“访问被拒绝”错误。在我测试过的任何其他浏览器(所有主要的现代浏览器)中都不会发生这种情况。

这是有道理的,因为我知道 Internet Explorer 要求您将所有将相互通信的窗口/框架的 document.domain 设置为相同的值。但是,我不知道有什么方法可以在我无法访问的文档上设置此值。

有没有人知道这样做的方法 - 以某种方式设置这个动态创建的 <iframe> 的 document.domain 属性?或者我不是从正确的角度看它 - 有没有另一种方法可以在不遇到这个问题的情况下实现我的目标?我确实需要在任何情况下使用 <iframe>,因为隔离/沙盒窗口对于此小部件的功能至关重要。

这是我的测试代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Document.domain Test</title>
    <script type="text/javascript">
      document.domain = 'onespot.com'; // set the page's document.domain
    </script>
  </head>
  <body>
    <p>This is a paragraph above the &lt;iframe&gt;.</p>
    <div id="placeholder"></div>
    <p>This is a paragraph below the &lt;iframe&gt;.</p>
    <script type="text/javascript">
      var iframe = document.createElement('iframe'), doc; // create <iframe> element
      document.getElementById('placeholder').appendChild(iframe); // append <iframe> element to the placeholder element
      setTimeout(function() { // set a timeout to give browsers a chance to recognize the <iframe>
        doc = iframe.contentWindow || iframe.contentDocument; // get a handle on the <iframe> document
        alert(doc);
        if (doc.document) { // HEREIN LIES THE PROBLEM
          doc = doc.document;
        }
        doc.body.innerHTML = '<h1>Hello!</h1>'; // add an element
      }, 10);
    </script>
  </body>
</html>

我已将其托管在:

http://troy.onespot.com/static/access_denied.html

如果您在 IE 中加载此页面,您将看到,在我调用 alert() 时,我确实在 <iframe> 的 window 对象上有一个句柄;我只是无法更深入地了解它的文档对象。

非常感谢您的任何帮助或建议!我将感谢任何能帮助我找到解决方案的人。

6个回答

如果在父页面中设置了 document.domain 属性,Internet Explorer 会给我一个“访问被拒绝”

叹。是的,这是一个 IE 问题(错误?很难说,因为没有针对这种不愉快的记录标准)。当您创建一个 srcless iframe 时,它​​会document.domain从父文档的location.host而不是它的document.domain. 那时你已经失去了很多,因为你无法改变它。

一个可怕的解决方法是设置src为 javascript: URL(呃!):

 iframe.src= "javascript:'<html><body><p>Hello<\/p><script>do things;<\/script>'";

但是出于某种原因,这样的文档无法document.domain在 IE 中设置自己的脚本(好老的“未指定错误”),因此您不能使用它来重新建立父级 (*) 之间的桥梁。可以使用它来编写整个文档 HTML,假设小部件在实例化后不需要与其父文档对话。

但是 iframe JavaScript URL 在 Safari 中不起作用,因此您仍然需要某种浏览器嗅探来选择要使用的方法。

*:由于某些其他原因,您可以在 IE 中document.domain从第二个文档设置,即由第一个文档编写的文档。所以这有效:

if (isIE)
    iframe.src= "javascript:'<script>window.onload=function(){document.write(\\'<script>document.domain=\\\""+document.domain+"\\\";<\\\\/script>\\');document.close();};<\/script>'";

在这一点上,丑陋程度对我来说太高了,我出去了。我会像大卫说的那样做外部 HTML。

这是一个测试页面,说明了我之前的评论:troy.onespot.com/static/access_denied_test.html
2021-03-20 13:03:30
@bobince:你真棒!实际上,经过更多的谷歌搜索后,我确实在昨晚深夜偶然发现了这种方法。事实上,我想我可能已经找到了一个更强大、可能更不笨拙的跨浏览器解决方案:telerik.com/community/forums/aspnet-ajax/editor/... - 请注意 Jeff Tucker 8 月 21 日的帖子。设置 < iframe> 的“src”属性为“javascript:void((function(){document.open();document.domain=\'tld.com\';document.close();})())”似乎做到这一点,并以跨浏览器的方式。它也适用于 Safari(至少 v3+)。
2021-03-24 13:03:30
顺便说一句,当 IE 试图保留 iframe 位置时,在 IE 中重新加载页面时会出现错误... argh。不知道有没有办法解决这个问题。
2021-03-24 13:03:30
@bobince:哦,伙计,你说得对。感谢您指出了这一点; 我很兴奋,第一次就成功了,我懒得重新加载。IE 试图保留 <iframe> 位置是什么意思?我必须找到解决方案,所以我会及时通知您 - 如果您碰巧找到任何东西,请执行同样的操作。
2021-03-28 13:03:30
哦,太好了!这样好多了!有趣的是,直接执行它应该可以工作......通常,一个 javascript: URL 将在其父级的上下文中执行,但 iframe src 似乎并非如此。您也可能会丢失void()调用,因为该函数已经返回undefined.
2021-03-29 13:03:30

是的,访问异常是由于document.domain必须在您的父级和 iframe 中匹配这一事实,在它们匹配之前,您将无法以编程方式设置document.domainiframe属性。

我认为您最好的选择是将页面指向您自己的模板:

iframe.src = '/myiframe.htm#' + document.domain;

在 myiframe.htm 中:

document.domain = location.hash.substring(1);
@Bungle:我认为这是你唯一的选择。
2021-03-26 13:03:30
是的,该解决方案确实需要该域上的另一个文件。不过,恐怕这是我能想到的最好的办法。
2021-04-03 13:03:30
谢谢,大卫 - 我很欣赏看似可靠的建议,但除非万不得已,否则我宁愿不采取这种方法。如果我理解正确,模板将需要与父页面位于同一个域中(对吗?),这将使我们的客户实现复杂化。理想情况下,实现应该像在其页面的 HTML 中插入几行 JavaScript 一样简单。
2021-04-10 13:03:30

好吧,我实际上有一个非常相似的问题,但有一个转折...说顶级站点是 a.foo.com - 现在我将文档域设置为 a.foo.com

然后在我创建/拥有的 iframe 中,我也将它设置为 a.foo.com

请注意,我也不能设置它们 foo.com b/c 页面中有另一个 iframe 指向 bafoo.com(它再次使用 a.foo.com,但我无法更改那里的脚本代码)

你会注意到我基本上将 document.domain 设置为它已经是什么......但我必须这样做才能访问我从 bafoo.com 提到的另一个 iframe

在我的框架内,在我设置域后,即使所有 iframe 都具有相同的设置,在 IE 6/7 中到达父级时仍然出现错误

还有其他一些非常奇怪的事情

在外部/顶层,如果我等待它的 onload 事件,并设置一个计时器,最终我可以深入到我需要访问的框架中......但我永远无法从下往上......而且我真的需要能够

此外,如果我将所有内容都设置为 foo.com(正如我所说,我不能这样做)它可以工作!但出于某种原因,当使用与 location.host 相同的值时......

我只是使用<iframe src="about:blank" ...></iframe>,它工作正常。

对于 IE,端口很重要。在域之间,它应该是相同的端口。