93

我正在尝试制作一个简单的postMessage示例...

  • 在 IE10 中
  • 窗口/选项卡之间(与 iframe 相比)
  • 跨越起源

删除这些条件中的任何一个,一切正常:-)

但据我所知,postMessage只有当两个窗口共享一个原点时,窗口间才会在 IE10 中工作。(嗯,事实上——而且奇怪的是——这种行为比这稍微宽松一些:共享一个主机的两个不同的起源似乎也可以工作)。

这是一个记录在案的错误吗?任何解决方法或其他建议?

(注意:这个问题涉及到问题,但它的答案是关于 IE8 和 IE9 -- 而不是 10)


更多细节+示例...

启动器页面演示

<!DOCTYPE html>
<html>
  <script>
    window.addEventListener("message", function(e){
      console.log("Received message: ", e);
    }, false);
  </script>
  <button onclick="window.open('http://jsbin.com/ameguj/1');">
    Open new window
  </button>
</html>

启动页面演示

<!DOCTYPE html>
<html>
  <script>
    window.opener.postMessage("Ahoy!", "*");
  </script>
</html>

这适用于: http: //jsbin.com/ahuzir/1——因为这两个页面都托管在同一个来源(jsbin.com)。但是将第二页移动到其他任何地方,它在 IE10 中失败。

4

8 回答 8

62

当我最初发布这个答案时我弄错了:它实际上在 IE10 中不起作用。显然人们出于其他原因发现这很有用,所以我将它留给后代。原答案如下:


值得注意的是:该答案中的链接链接到的状态postMessage不是 IE8 和 IE9 中单独窗口的交叉来源——但是,它也是在 2009 年 IE10 出现之前编写的。所以我不会认为它在 IE10 中已修复。

至于postMessage它本身,http://caniuse.com/#feat=x-doc-messaging明显表明它在 IE10 中仍然存在问题,这似乎与您的演示相匹配。caniuse 页面链接到这篇文章,其中包含一个非常相关的引用:

Internet Explorer 8+ 部分支持跨文档消息传递:它目前适用于 iframe,但不适用于新窗口。但是,Internet Explorer 10 将支持 MessageChannel。Firefox 当前支持跨文档消息传递,但不支持 MessageChannel。

所以你最好的选择可能是有一个MessageChannel基于代码路径,postMessage如果不存在则回退。它不会为您提供 IE8/IE9 支持,但至少它可以与 IE10 一起使用。

文档MessageChannel:http: //msdn.microsoft.com/en-us/library/windows/apps/hh441303.aspx

于 2013-05-01T05:49:47.930 回答
30

在与启动器相同的主机上创建代理页面。代理页面有一个iframe源设置为远程页面。跨域 postMessage 现在可以在 IE10 中运行,如下所示:

  • 远程页面用于window.parent.postMessage将数据传递给代理页面。由于它使用 iframe,因此受 IE10 支持
  • 代理页面用于window.opener.postMessage将数据传递回启动器页面。由于这是在同一个域上 - 没有跨域问题。如果您不想使用 postMessage,它也可以直接在启动器页面上调用全局方法 - 例如。window.opener.someMethod(data)

示例(所有 URL 均为虚构)

启动器页面位于http://example.com/launcher.htm

<!DOCTYPE html>
<html>
    <head>
        <title>Test launcher page</title>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>

    <script>
        function log(msg) {
            if (!msg) return;

            var logger = document.getElementById('logger');
            logger.value += msg + '\r\n';
        }            

        function toJson(obj) {
            return JSON.stringify(obj, null, 2);
        }

        function openProxy() {
            var url = 'proxy.htm';
            window.open(url, 'wdwProxy', 'location=no');
            log('Open proxy: ' + url);
        }

        window.addEventListener('message', function(e) {
            log('Received message: ' + toJson(e.data));
        }, false);
    </script>
    
    <button onclick="openProxy();">Open remote</button> <br/>
    <textarea cols="150" rows="20" id="logger"></textarea>

    </body>
</html>

代理页面位于http://example.com/proxy.htm

<!DOCTYPE html>
<html>
    <head>
        <title>Proxy page</title>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>

    <script>
        function toJson(obj) {
            return JSON.stringify(obj, null, 2);
        }

        window.addEventListener('message', function(e) {
            console.log('Received message: ' + toJson(e.data));

            window.opener.postMessage(e.data, '*');
            window.close(self);
        }, false);
    </script>

    <iframe src="http://example.net/remote.htm" frameborder="0" height="300" width="500" marginheight="0" marginwidth="0" scrolling="auto"></iframe>

    </body>
</html>

远程页面在http://example.net/remote.htm

<!DOCTYPE html>
<html>
    <head>
        <title>Remote page</title>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>

    <script>
        function remoteSubmit() {
            var data = {
                message: document.getElementById('msg').value
            };

            window.parent.postMessage(data, '*');
        }
    </script>
    
    <h2>Remote page</h2>

    <input type="text" id="msg" placeholder="Type a message" /><button onclick="remoteSubmit();">Close</button>

    </body>
</html>
于 2014-07-01T13:39:36.833 回答
29

== 2020 年没有 iframe 的工作解决方案 ==

基于纠结的答案,我使用以下代码段在 IE11 [和模拟 IE10 模式] 中取得了成功:

var submitWindow = window.open("/", "processingWindow");
submitWindow.location.href = 'about:blank';
submitWindow.location.href = 'remotePage to communicate with';

然后我能够使用典型的 postMessage 堆栈进行通信,我在我的场景中使用了一个全局静态信使(尽管我认为它没有任何意义,我还附加了我的信使类)

    var messagingProvider = {
        _initialized: false,
        _currentHandler: null,

        _init: function () {
            var self = this;
            this._initialized = true;
            var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
            var eventer = window[eventMethod];
            var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

            eventer(messageEvent, function (e) {
                var callback = self._currentHandler;
                if (callback != null) {
                    var key = e.message ? "message" : "data";
                    var data = e[key];
                    callback(data);
                }
            }, false);
        },

        post: function (target, message) {
            target.postMessage(message, '*');
        },

        setListener: function (callback) {
            if (!this._initialized) {
                this._init();
            }

            this._currentHandler = callback;
        }
    }

无论我多么努力,我都无法在 IE9 和 IE8 上运行

我的工作配置:
IE 版本:11.0.10240.16590,更新版本:11.0.25 (KB3100773)

于 2016-04-14T17:29:52.220 回答
2

基于 LyphTEC 和 Akrikos 的答案,另一种解决方法是<iframe>在空白弹出窗口内创建一个,这避免了需要单独的代理页面,因为空白弹出窗口与其开启者具有相同的来源。

启动器页面位于http://example.com/launcher.htm

<html>
  <head>
    <title>postMessage launcher</title>
    <script>
      function openWnd() {
        var w = window.open("", "theWnd", "resizeable,status,width=400,height=300"),
            i = w.document.createElement("iframe");

        i.src = "http://example.net/remote.htm";
        w.document.body.appendChild(i);

        w.addEventListener("message", function (e) {
          console.log("message from " + e.origin + ": " + e.data);

          // Send a message back to the source
          e.source.postMessage("reply", e.origin);
        });
      }
    </script>
  </head>
  <body>
    <h2>postMessage launcher</h2>
    <p><a href="javascript:openWnd();">click me</a></p>
  </body>
</html>

远程页面在http://example.net/remote.htm

<html>
  <head>
    <title>postMessage remote</title>
    <script>
      window.addEventListener("message", function (e) {
        alert("message from " + e.origin + ": " + e.data);
      });

      // Send a message to the parent window every 5 seconds
      setInterval(function () {
        window.parent.postMessage("hello", "*");
      }, 5000);
    </script>
  </head>
  <body>
    <h2>postMessage remote</h2>
  </body>
</html>

我不确定这有多脆弱,但它在 IE 11 和 Firefox 40.0.3 中运行。

于 2015-09-08T08:06:16.157 回答
1

现在,(2014 年 9 月 2 日),最好的办法是使用 msdn 博客文章中所述的代理框架,该文章详细介绍了此问题的解决方法:https ://blogs.msdn.microsoft.com/ieinternals/2009 /09/15/html5-implementation-issues-in-ie8-and-later/

这是工作示例: http: //www.debugtheweb.com/test/xdm/origin/

您需要在您的页面上设置一个与弹出窗口具有相同来源的代理框架。使用 将信息从弹出窗口发送到代理框架window.opener.frames[0]。然后使用 postMessage 从代理框架到主页。

于 2014-10-02T22:33:09.777 回答
1

此解决方案涉及将站点添加到 Internet Explorer 的受信任站点,而不是本地 Intranet 站点。我在 Windows 10/IE 11.0.10240.16384、Windows 10/Microsoft Edge 20.10240.16384.0 和 Windows 7 SP1/IE 10.0.9200.17148 中测试了这个解决方案。该页面不得包含在 Intranet 区域中。

所以打开 Internet Explorer 配置(工具 > Internet 选项 > 安全 > 受信任的站点 > 站点),并添加页面,这里我使用 * 来匹配所有子域。确保该页面未列在本地 Intranet 站点(工具 > Internet 选项 > 安全 > 本地 Intranet > 站点 > 高级)中。重新启动浏览器并再次测试。

在 Internet Explorer 中添加到受信任的站点

Windows 10/Microsoft Edge中,您将在控制面板 > Internet 选项中找到此配置。

更新

如果这不起作用,您可以尝试在工具 > Internet 选项 > 高级设置 > 重置 Internet Explorer 设置中重置所有设置,然后重置:谨慎使用!然后,您将需要重新启动系统。之后将站点添加到受信任的站点。

查看您的页面在文件 > 属性中的哪个区域或使用右键单击。

Internet Explorer 中的页面属性

更新

我在公司内部网中,有时可以工作,有时不能(自动配置?我什至开始责怪公司代理)。最后我使用了这个解决方案https://stackoverflow.com/a/36630058/2692914

于 2017-01-25T22:37:24.970 回答
0

这个 Q 很旧,但这就是 easyXDM 的用途,当您检测到不支持 html5 .postMessage 的浏览器时,也许可以将其作为潜在的后备来检查:

https://easyxdm.net/

它使用 VBObject 包装器和您永远不想处理的所有类型的东西来在窗口或框架之间发送跨域消息,其中 window.postMessage 对各种 IE 版本失败(可能还有边缘,仍然不确定 100% 的支持Edge 有,但似乎也需要 .postMessage 的解决方法)

于 2019-12-12T19:37:44.037 回答
-3

MessageChannel 不适用于窗口/选项卡之间的 IE 9-11,因为它依赖于 postMessage,在这种情况下它仍然被破坏。“最佳”解决方法是通过 window.opener 调用函数(即 window.opener.somefunction("somedata") )。

更详细的解决方法here

于 2014-05-07T15:41:56.870 回答