[IE11] 关于 window.opener & window.postMessage

sddtc 于 2018-12-03 发布

背景

最近在开发的时候遇见一些有趣的问题,其中之一就是在 IE11 上有些功能不可用,怎么说呢,具体要讲一下关于 window.openerwindow.opener.postMessage 这两个特性.
解决问题的思路便是,不论你怎么想的,构造一个 demo 是有效且强大的方式之一。

使用场景: 在登录之后的跳转成功页面需要发一条消息给登录界面,告诉登录界面登录成功。
例如登录页面的URL: https://test.com/oauth2/login?redirect_uri=https://client1.com/callback

1.在 https://test.com/oauth2/login 页面输入用户名,密码
2.点击登录按钮
3.登录成功会跳转到 redirect_uri 页面,那么这时在 https://client1.com/callback 页面使用window.opener.postMessage 发送一条消息
4.https://test.com/oauth2/login 页面会收到消息来进行后续的处理

构建一个 DEMO

需要构建两个页面, 登录页面A与跳转页面B:

页面A:

<!DOCTYPE html>
<head>
  <title>main</title>
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <script>
    var interval;
    function msgHandler(ev) {
        if (ev.data.message === "deliverResult") {
          console.log("result: " + ev.data.result);
          ev.source.close();
          clearInterval(interval);
          window.removeEventListener("message", msgHandler);
        }
    }
    function signin() {
      var child = window.open("https://test.com/oauth2/login?redirect_uri=https://client1.com/callback", "_blank");
      interval = setInterval(function() {
        try {
          if (child.closed) {
            console.log("Window was closed....");
            clearInterval(interval);
            window.removeEventListener("message", msgHandler);
            return;
          }
        } catch (e) {
          console.log("ERROR", e);
        }
      }, 500);
      window.addEventListener("message", msgHandler);
    }
  </script>
</head>
<body>
  <button onclick="signin()">Sign in</button>
</body>

点击 Sign in 按钮时,首先会打开一个新的 tab 页面,并在当前 window 添加一个监听器来监听从子页面过来的消息,如果子页面关闭,则清除监听器

页面B:

<!DOCTYPE html>
<head>
<title>child</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<script>
    window.opener && window.opener.postMessage({ message: "deliverResult", result: true }, "*");
</script>
</head>
<body>
<a href="http://www.example.com">Go to example.com</a>
Then click the browser Back button when ready.
</body>

子页面被打开时,会发送一条消息给父页面。

其中,
window.opener 兼容性:MDN-window.opener
window.postmessage 兼容性:MDN-window.postmessage

这个 Demo 可以完成一个简单的页面间通讯,并且工作于 Chrome 和 Safari, 虽然 MDN 上 window.opener 对于 IE 的支持是一个问号,但是实际上它是工作的。
然而在 IE11 上面”工作”的含义是父页面的domain和子页面的domain是在同一域名下。
在跨域的情况下,子页面 window.postmessage 的消息是无法发送给父页面的。

举例来说:
页面A和页面B运行在一台服务器上,访问域名是: https://serverA.com
页面A需要打开一个登陆页面链接为: https://test.com/oauth2/login?redirect_uri=https://client1.com/callback
而跳转页面是: https://client1.com/callback
这种情况下,我们会处在一个跨域的情况下发送消息,IE11不支持也就是页面A收不到跳转链接页面的消息,那么问题来了,为什么要解决这个问题?因为 IE11 的用户是我们不能放弃的用户群。如果你不在意这部分用户,可以不用太过关注。

解决方法

方法一

保证页面A需要打开的登录页面链接和页面A在同一域名之下,也就是在页面A所在的服务器上添加一个路由,例如: https://serverA.com/gotoSignin, 而这个路由可以打开一个跨域的链接:https://test.com/oauth2/login?redirect_uri=https://client1.com/callback. 也就是说:只要保证window.open 了一个同域名的链接,那么接下来无论中间会如何跳转,哪怕跳到天上去,父页面也可以收到子页面的消息。

优点: 依然可以使用 window.openerwindow.postMessage
缺点: 需要额外添加一个服务器端的路由

方法二

使用 window.localStorage 保证子页面写入数据,父页面使用定时器循环读取,仍然需要保证 window.open 在同域名下。
类似的,写 cookie 也是可以的,只是我们不建议采用的原因取决于具体的使用场景:如果传输的信息安全性很高,我们不希望写入浏览器内。

以上

工作在前端的话,浏览器支持问题是一件有趣的事,也许你实现了功能,突然恍然大悟这玩意儿在 IEXX 下根本不支持。
解决的方法有很多,重要的走出你的第一步,构建一个 demo.