16
votes

I control the content of an iframe which is embedded in a page from another domain. Is there any way for javascript in my iframe to make changes to the parent's DOM?

For example, I would like to have my iframed script add a bunch of html elements to the parent DOM. This seems like a pretty tall order - thoughts?

Edit: There exists a technique called "Fragment ID Messaging" which might be a way to communicate between cross-domain iframes.

Edit: Also, Firefox 3.5, Opera, Chrome (etc) seem to be adopting the html5 "postMessage" api, which allows secure, cross-domain data transmission between frames, iframes and popups. It works like an event system. IE8 supports this feature, apparently, which is perhaps a little surprising.

Summary: No, you can't directly access/edit the DOM of a page from another domain. But you can communicate with it, and it can co-operate to make the changes you want.

5
The currently accepted answer was correct in 2009, but times have changed. Stefan Steiger's is better and it might be worth changing your accepted answer to it.Quentin
Thanks @Quentin, I will consider this.aaaidan

5 Answers

20
votes

Hate to say it but I'm like 99% sure that ain't happening directly because of security.

You can try it out here.

bhh

11
votes

It is possible.

You were right to mention postMessage in your edits. For those looking, there is a great backwards-compatible, javascript-only way to communicate across domains. Short, easy code as well. Perfect solution? As long as you can request modifications to the parent and the child:

http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage/

5
votes

Yes you can.
You can implement window.postMessage to communicate accross iframes and/or windows across domains.
But you need to do it in an asynchronous way.
If you need it synchronously, you need to implement wrappers around those asynchronous methods.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title></title>

    <!--
    <link rel="shortcut icon" href="/favicon.ico">


    <link rel="start" href="http://benalman.com/" title="Home">

    <link rel="stylesheet" type="text/css" href="/code/php/multi_file.php?m=benalman_css">

    <script type="text/javascript" src="/js/mt.js"></script>
    -->
    <script type="text/javascript">
        // What browsers support the window.postMessage call now?
        // IE8 does not allow postMessage across windows/tabs
        // FF3+, IE8+, Chrome, Safari(5?), Opera10+

        function SendMessage()
        {
            var win = document.getElementById("ifrmChild").contentWindow;

            // http://robertnyman.com/2010/03/18/postmessage-in-html5-to-send-messages-between-windows-and-iframes/


            // http://stackoverflow.com/questions/16072902/dom-exception-12-for-window-postmessage
            // Specify origin. Should be a domain or a wildcard "*"

            if (win == null || !window['postMessage'])
                alert("oh crap");
            else
                win.postMessage("hello", "*");
            //alert("lol");
        }



        function ReceiveMessage(evt) {
            var message;
            //if (evt.origin !== "http://robertnyman.com")
            if (false) {
                message = 'You ("' + evt.origin + '") are not worthy';
            }
            else {
                message = 'I got "' + evt.data + '" from "' + evt.origin + '"';
            }

            var ta = document.getElementById("taRecvMessage");
            if (ta == null)
                alert(message);
            else
                document.getElementById("taRecvMessage").innerHTML = message;

            //evt.source.postMessage("thanks, got it ;)", event.origin);
        } // End Function ReceiveMessage




        if (!window['postMessage'])
            alert("oh crap");
        else {
            if (window.addEventListener) {
                //alert("standards-compliant");
                // For standards-compliant web browsers (ie9+)
                window.addEventListener("message", ReceiveMessage, false);
            }
            else {
                //alert("not standards-compliant (ie8)");
                window.attachEvent("onmessage", ReceiveMessage);
            }
        }
    </script>


</head>
<body>

    <iframe id="ifrmChild" src="child.htm" frameborder="0" width="500" height="200" ></iframe>
    <br />


    <input type="button" value="Test" onclick="SendMessage();" />

</body>
</html>

Child.htm

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title></title>

    <!--
    <link rel="shortcut icon" href="/favicon.ico">


    <link rel="start" href="http://benalman.com/" title="Home">

    <link rel="stylesheet" type="text/css" href="/code/php/multi_file.php?m=benalman_css">

    <script type="text/javascript" src="/js/mt.js"></script>
    -->

    <script type="text/javascript">
        /*
        // Opera 9 supports document.postMessage() 
        // document is wrong
        window.addEventListener("message", function (e) {
            //document.getElementById("test").textContent = ;
            alert(
                e.domain + " said: " + e.data
                );
        }, false);
        */

        // https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage
        // http://ejohn.org/blog/cross-window-messaging/
        // http://benalman.com/projects/jquery-postmessage-plugin/
        // http://benalman.com/code/projects/jquery-postmessage/docs/files/jquery-ba-postmessage-js.html

        // .data – A string holding the message passed from the other window.
        // .domain (origin?) – The domain name of the window that sent the message.
        // .uri – The full URI for the window that sent the message.
        // .source – A reference to the window object of the window that sent the message.
        function ReceiveMessage(evt) {
            var message;
            //if (evt.origin !== "http://robertnyman.com")
            if(false)
            {
                message = 'You ("' + evt.origin + '") are not worthy';
            }
            else
            {
                message = 'I got "' + evt.data + '" from "' + evt.origin + '"';
            }

            //alert(evt.source.location.href)

            var ta = document.getElementById("taRecvMessage");
            if(ta == null)
                alert(message);
            else
                document.getElementById("taRecvMessage").innerHTML = message;

            // http://javascript.info/tutorial/cross-window-messaging-with-postmessage
            //evt.source.postMessage("thanks, got it", evt.origin);
            evt.source.postMessage("thanks, got it", "*");
        } // End Function ReceiveMessage




        if (!window['postMessage'])
            alert("oh crap");
        else {
            if (window.addEventListener) {
                //alert("standards-compliant");
                // For standards-compliant web browsers (ie9+)
                window.addEventListener("message", ReceiveMessage, false);
            }
            else {
                //alert("not standards-compliant (ie8)");
                window.attachEvent("onmessage", ReceiveMessage);
            }
        }
    </script>


</head>
<body style="background-color: gray;">
    <h1>Test</h1>

    <textarea id="taRecvMessage" rows="20" cols="20" ></textarea>

</body>
</html>

Here, you would modify the child to send postmessages to parent. e.g. in child.htm, you do

window.parent.postMessage("alert(document.location.href); document.location.href = 'http://www.google.com/ncr'", "*");

and in parent, you do (in receiveMessage) eval(evt.data); Not that using eval is insecure, so you would instead pass an enum, and call the corresponding function that you need to put on the parent page.

0
votes

I guess you will run to security problems without using a proxy. Proxies can be very to use. You can try one of those:

(1) a PHP based proxy (be careful cause there are a lot of ads between useful links)

(2) a Apache .htaccess proxy - just create a subdirectory proxy in your domain and put there a .htaccess file containing:

RewriteEngine on
RewriteRule ^(.*)$ http://picasaweb.google.com/$1 [P,L] 

Put the other domain name in place of picasaweb.google.com

Personally I prefer using the Apache proxy

0
votes

For AJAX, the server could return the header Access-Control-Allow-Origin: * to allow cross-domain access. Maybe it works also for IFRAMEs.