如何跨域访问父级DOM?使用<iframe>和JavaScript。

16

我控制着嵌入在另一个域名页面中的iframe的内容。我的iframe中的javascript是否有办法修改父页面的DOM?

例如,我想让我的iframed脚本向父级DOM添加一堆HTML元素。这似乎是一个相当高的要求-有什么想法吗?

编辑:存在一种称为“Fragment ID Messaging”的技术,可能是在跨域iframe之间进行通信的一种方式。

编辑:此外,Firefox 3.5、Opera、Chrome等浏览器似乎正在采用html5的"postMessage" api,它允许安全地在帧、iframe和弹出窗口之间进行跨域数据传输。它的工作原理类似于事件系统。IE8显然也支持此功能,这可能有点令人惊讶。

总结:不能直接访问/编辑来自另一个域的页面的DOM。但是,您可以与其通信,并且它可以合作以实现您想要的更改。


当前被接受的答案在2009年是正确的,但现在情况已经改变了。Stefan Steiger的更好,也许值得将你接受的答案改为它。 - Quentin
谢谢@Quentin,我会考虑的。 - aaaidan
5个回答

21

很遗憾,我99%确定这不会直接发生是因为安全问题。

你可以在这里尝试一下。

bhh


1
谢谢,那个链接很好地展示了问题。 - aaaidan
你可以在子域名中嵌入一个iframe,指向父域名,这样任何脚本都可以访问parent.parent,因为它们在同一个域名下。一些广告公司会要求你在你的网站上托管一个文件(内部iframe),以便允许他们调整广告大小。 - Hayden Crocker
请阅读下面 @Stefan Steiger 的答案。您可以使用 postMessage() 在跨域框架之间进行通信。当然,您必须拥有这两个域名。 - Marco Panichi

11

5

是的,您可以这样做。
您可以实现window.postMessage来跨域通信,包括在iframes和/或窗口之间进行通信。
但是需要以异步方式执行。
如果您需要同步执行,您需要在这些异步方法周围实现包装器。

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

在这里,你需要修改子页面,使其向父页面发送postmessages。 例如,在child.htm中,你需要这样做:
window.parent.postMessage("alert(document.location.href); document.location.href = 'http://www.google.com/ncr'", "*");

在父页面中,在receiveMessage函数中使用以下代码:eval(evt.data);。需要注意的是,使用eval存在安全风险,因此最好使用枚举方式传递信息,并调用相应的函数在父页面上执行。

子iframe的加载在混合模式环境下无法正常工作。例如,主页面使用https,而子页面在iframe中使用http。 - lmiguelmh
1
@lmiguelmh:一个HTTP页面本来就不应该在HTTPS页面中加载。 - Stefan Steiger

0
我猜如果没有使用代理,你就会遇到安全问题。代理可以很容易地使用。你可以尝试以下其中之一:
(1)基于 PHP 的代理(要小心,因为有许多广告在有用链接之间)
(2)Apache .htaccess 代理 - 只需在你的域中创建一个子目录"proxy"并放置一个包含以下内容的".htaccess"文件:
RewriteEngine on
RewriteRule ^(.*)$ http://picasaweb.google.com/$1 [P,L] 

将其他域名放在picasaweb.google.com的位置。
个人而言,我更喜欢使用Apache代理。

感谢您的回答,warpech。我认为我的编辑混淆了我的原始问题,即如何从另一个域中的iframe内更改父页面上的dom。简短的答案似乎是“你不能”。因此,我现在正在探索帧间通信方法,服务器端代理肯定是其中之一。谢谢! - aaaidan

0
对于 AJAX,服务器可以返回头部 Access-Control-Allow-Origin: * 以允许跨域访问。也许它也适用于 IFRAME。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接