"Access is denied" JavaScript error when trying to access the document object of a programmatically-created <iframe> (IE-only)

205,454

Solution 1

if the document.domain property is set in the parent page, Internet Explorer gives me an "Access is denied"

Sigh. Yeah, it's an IE issue (bug? difficult to say as there is no documented standard for this kind of unpleasantness). When you create a srcless iframe it receives a document.domain from the parent document's location.host instead of its document.domain. At that point you've pretty much lost as you can't change it.

A horrendous workaround is to set src to a javascript: URL (urgh!):

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

But for some reason, such a document is unable to set its own document.domain from script in IE (good old “unspecified error”), so you can't use that to regain a bridge between the parent(*). You could use it to write the whole document HTML, assuming the widget doesn't need to talk to its parent document once it's instantiated.

However iframe JavaScript URLs don't work in Safari, so you'd still need some kind of browser-sniffing to choose which method to use.

*: For some other reason, you can, in IE, set document.domain from a second document, document.written by the first document. So this works:

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

At this point the hideousness level is too high for me, I'm out. I'd do the external HTML like David said.

Solution 2

Well yes, the access exception is due to the fact that document.domain must match in your parent and your iframe, and before they do, you won't be able to programmatically set the document.domain property of your iframe.

I think your best option here is to point the page to a template of your own:

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

And in myiframe.htm:

document.domain = location.hash.substring(1);

Solution 3

well i actually have a very similar problem, but with a twist... say the top level site is a.foo.com - now i set document domain to a.foo.com

then in the iframe that i create / own,i also set it too a.foo.com

note that i cant set them too foo.com b/c there is another iframe in the page pointed to b.a.foo.com (which again uses a.foo.com but i cant change the script code there)

youll note that im essentially setting document.domain to what it already would be anyway...but i have to do that to access the other iframe i mentioned from b.a.foo.com

inside my frame, after i set the domain, eventhough all iframes have the same setting, i still get an error when reaching up into the parent in IE 6/7

there are other things that r really bizaree

in the outside / top level, if i wait for its onload event, and set a timer, eventually i can reach down into the frame i need to access....but i can never reach from bottom up... and i really need to be able to

also if i set everything to be foo.com (which as i said i cannot do) IT WORKS! but for some reason, when using the same value as location.host....it doesnt and its freaking killing me.....

Solution 4

for IE, the port matters. In between domains, it should be same port.

Solution 5

I just use <iframe src="about:blank" ...></iframe> and it works fine.

Share:
205,454

Related videos on Youtube

Bungle
Author by

Bungle

Front-End Developer. #SOreadytohelp

Updated on July 05, 2022

Comments

  • Bungle
    Bungle almost 2 years

    I have project in which I need to create an <iframe> element using JavaScript and append it to the DOM. After that, I need to insert some content into the <iframe>. It's a widget that will be embedded in third-party websites.

    I don't set the "src" attribute of the <iframe> since I don't want to load a page; rather, it is used to isolate/sandbox the content that I insert into it so that I don't run into CSS or JavaScript conflicts with the parent page. I'm using JSONP to load some HTML content from a server and insert it in this <iframe>.

    I have this working fine, with one serious exception - if the document.domain property is set in the parent page (which it may be in certain environments in which this widget is deployed), Internet Explorer (probably all versions, but I've confirmed in 6, 7, and 8) gives me an "Access is denied" error when I try to access the document object of this <iframe> I've created. It doesn't happen in any other browsers I've tested in (all major modern ones).

    This makes some sense, since I'm aware that Internet Explorer requires you to set the document.domain of all windows/frames that will communicate with each other to the same value. However, I'm not aware of any way to set this value on a document that I can't access.

    Is anyone aware of a way to do this - somehow set the document.domain property of this dynamically created <iframe>? Or am I not looking at it from the right angle - is there another way to achieve what I'm going for without running into this problem? I do need to use an <iframe> in any case, as the isolated/sandboxed window is crucial to the functionality of this widget.

    Here's my test code:

    <!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>
    

    I've hosted it at:

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

    As you'll see if you load this page in IE, at the point that I call alert(), I do have a handle on the window object of the <iframe>; I just can't get any deeper, into its document object.

    Thanks very much for any help or suggestions! I'll be indebted to whomever can help me find a solution to this.

    • mbomb007
      mbomb007 almost 6 years
      The link for troy.onespot is dead.
  • Bungle
    Bungle over 14 years
    Thanks, David - I appreciate what appears to be a solid suggestion, but I'd rather not take this approach unless it's a last resort. If I understand correctly, the template would need to live on the same domain as the parent page (is that right?), and that would complicate implementation for our customers. Ideally the implementation should be as simple as inserting a few lines of JavaScript in their pages' HTML.
  • Bungle
    Bungle over 14 years
    Deniss, good suggestion, and thank you - I gave this a shot (see <troy.onespot.com/static/access_denied_jquery.html>) but got a similar error; this time it's "Permission denied" within the jQuery script. I suspect it's the same or a similar roadblock.
  • Bungle
    Bungle over 14 years
    Sorry, that URL got mangled. Try: troy.onespot.com/static/access_denied_jquery.html
  • David Hedlund
    David Hedlund over 14 years
    Yes, the solution does require another file on that very domain. I'm afraid that's the best I can come up with, though.
  • Tim Down
    Tim Down over 14 years
    @Bungle: I think it's your only option.
  • Tim Down
    Tim Down over 14 years
    Why would jQuery have magical access to the iframe's document?
  • Bungle
    Bungle over 14 years
    @Tim Down: I don't think the assumption was that jQuery would have magical access; rather, jQuery often has some nifty tricks up its sleeve to solve cross-browser issues, and might have already implemented a workaround. I agree it didn't bode well, but I think it was a good suggestions and worth a shot.
  • Bungle
    Bungle over 14 years
    @bobince: You're awesome! I actually did stumble upon this very approach late last night after a lot more Googling. In fact, I think I might have found an even more robust, and potentially less kludgy cross-browser solution: telerik.com/community/forums/aspnet-ajax/editor/… - notice Jeff Tucker's post from August 21. Setting the <iframe>'s "src" attribute to "javascript:void((function(){document.open();document.domain‌​=\'tld.com\';documen‌​t.close();})())" seems to do the trick, and in a cross-browser way. It also works in Safari (at least v3+).
  • Bungle
    Bungle over 14 years
    Here's a test page illustrating my previous comment: troy.onespot.com/static/access_denied_test.html
  • Amit Patil
    Amit Patil over 14 years
    Oh great! That's much better! Interesting that doing it directly should work... normally, a javascript: URL would be executed in the context of its parent, but that seems not to be the case with iframe src. You can probably also lose the void() call, since the function already returns undefined.
  • Amit Patil
    Amit Patil over 14 years
    Incidentally you get an error reloading the page in IE with this, as IE tries to retain the iframe location... argh. Dunno if there's a way around that.
  • Bungle
    Bungle over 14 years
    @bobince: Oh man, you're right. Thanks for pointing that out; I was so excited that it worked the first time that I didn't bother to reload. What do you mean by IE trying to retain the <iframe> location? I've got to find a solution to this so I'll keep you posted - please do the same if you happen to find anything.
  • vit
    vit almost 13 years
    Thanks. This seems to have fixed the problem for me.
  • Danyal Aytekin
    Danyal Aytekin over 11 years
    I think you're solving a different problem.
  • Christophe
    Christophe over 11 years
    The OP is building a third party widget. You cannot expect every visitor to follow all these steps to get the widget displayed.
  • Jason
    Jason about 11 years
    @bobince - any improvement to this? see question here: stackoverflow.com/questions/14879192/…
  • frostymarvelous
    frostymarvelous over 10 years
    weird thing is, this was the same thing I was using. was working fine, now all of a sudden, I keep getttig access denied errors
  • Jean
    Jean almost 9 years
    A solution may be to insert a different ID in the iframe's src attribute everytime you load the frame, for example new Date().getTime(). Horrible but that seems to work.
  • Neil Monroe
    Neil Monroe over 7 years
    This really isn't an answer. It's only a suggestion and would probably be better as a comment on the original post.