• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • html5 跨文档通讯

    版本:HTML5

    对窗口对象的message事件进行监听,使用window对象的postMessage()方法向其他窗口发送消息。

    跨文档消息传送(cross-document messaging),简称XDM,指的是来自不同域的页面间传递消息。

    XDM的核心是postMessage()方法。目的:向另一个地方传递数据。对于XDM而言,“另一个地方”指的是包含在当前页面中的<iframe>元素,或者由当前页面弹出的窗口。

    postMessage()方法接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。第二个参数对保障安全通信非常重要,可以防止浏览器把消息发送到不安全的地方。

    接收到XDM消息时,会触发window对象的message事件。这个事件是以异步形式触发的,因此从发送消息到接受消息(触发接受窗口的message事件)可能要经过一段时间的延迟。

    有了XDM,包含<iframe>的页面可以确保自身不受恶意内容的侵扰,因为它只通过XDM为嵌入的框架通信。而XDM也可以来自相同域的页面间使用。

    如果要实现从一个页面向到另一个页面传递消息,使用html5的API就可以实现。postMessage()就是用来试下跨文档消息传递的,不过这个API有局限,只能新开的页面传递消息(内嵌也可以)。

    浏览器支持

    Internet Explorer 8 以上版本支持

    postMessage()

    otherWindow.postMessage(message, targetOrigin, [transfer]);
    • otherWindow是目标窗口的引用,在当前场景下就是iframe.contentWindow;
    • message是发送的消息,在Gecko 6.0之前,消息必须是字符串,而之后的版本可以做到直接发送对象而无需自己进行序列化。
    • targetOrigin表示设定目标窗口的origin,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。对于保密性的数据,设置目标窗口origin非常重要。
    • transfer可选参数,是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

    当postMessage()被调用的时,一个消息事件就会被分发到目标窗口上。该接口有一个message事件,该事件有几个重要的属性:

  • data:顾名思义,是传递来的message。
  • source:发送消息的窗口对象。
  • origin:发送消息窗口的源(协议+主机+端口号)。
  • otherwindow为要发送窗口 对象的引用,可以通过window.open()方法返回该对象,或通过对window.frames数组指定序号(index)或名字的方式来返回单个 frame所属性的窗口对象。

    那么,当iframe初始化后,可以通过下面代码获取到iframe的引用并发送消息:

    // 注意这里不是要获取iframe的dom引用,而是iframe window的引用
    const iframe = document.getElementById('myIFrame').contentWindow;
    iframe.postMessage('hello world', 'https://www.lanmper/cn');
    

    在iframe中,通过下面代码即可接收到消息。

    window.addEventListener('message', msgHandler, false);
    

    在接收时,可以根据需要,对消息来源origin做一下过滤,避免接收到非法域名的消息导致的xss攻击。

    实例

    <!DOCTYPE html>  
    <html>  
           <head>  
               <meta charset="UTF-8"/>  
               <title>跨文档消息传输示例主文档</title>  
               <script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>  
               <script type="text/javascript">  
        $(function() {   
           // 监听message事件。   
           window.addEventListener("message", function(event) {   
               // 忽略指定URL之外的页面发送的消息。   
               if(event.origin != "https://www.lanmper/cn") return;   
               alert(event.data); // 显示消息。   
           }, false);   
              
           $("#iframeContent").load(function(event) {   
                   // 向子页面发送消息   
               this[0].postMessage("Hello", "https://www.lanmper/cn/test/");   
           });   
        });   
               </script>  
           </head>  
           <body>  
               <header>  
                   <h1>跨域通信示例</h1>  
               </header>  
               <iframe id="iframeContent" width="400" src="https://www.lanmper/cn/test/"></iframe>  
           </body>  
    </html>
    

    子页面中的代码

    <!DOCTYPE html>  
    <html>  
           <head>  
               <meta charset="UTF-8"/>  
               <script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>  
               <script type="text/javascript">  
        $(function() {   
           window.addEventListener("message", function(event) {   
               if(event.origin != "https://www.lanmper.cn") return;   
               $("#console").append(event.origin).append("传来的消息:").append(event.data);   
                       // 向主页面发送消息。   
               event.source.postMessage("Hello, there is :" + this.location, event.origin);   
           }, false);   
        });   
               </script>  
           </head>  
           <body>  
               <p>这是iframe中的内容。</p>  
               <div id="console"></div>  
           </body>  
    </html>
    
    • 通过对window对象的message事件进行监听,可以接收消息。
    • 通过访问message事件的origin属性,可以获取消息的发送源。注意:发送源与网站的URL地址不是一个概念,发送源只包括域名与 端口号,为了不接收其他源恶意发送过来的消息,最好对发送源做检查。
    • 通过访问message事件的data属性,可以取得消息内容(可以是任何JavaScript对象,使用JSON)。
    • 使用postMessage()方法发送消息。
    • 通过访问message事件的source属性,可以获取消息发送源的窗口的代理对象。

    一个页面内嵌另一个页面

    // index.jsp页面(源页面)
    <iframe src="http://localhost:8090/a"></iframe>
    <script type="text/javascript">
    // 跨域发送数据到目标源
    window.onload = function () 
    {
        document.getElementsByTagName("iframe")[0].contentWindow.postMessage({name: 'wujiang'}, "http://localhost:8090");
    }
    // 监听目标源发送过来的数据
    window.addEventListener('message', function (e)
    {
        console.log('http://localhost:8090/a.ftl ===> http://localhost:8080/index.jsp');
        console.log(e);
    })
    </script>
    
    <script type="text/javascript">
    // a.ftl页面(目标页面)
    // 监听其他窗口是否发送数据
    if (window.addEventListener) 
    {
    window.addEventListener('message', function (e) {
        if (e.origin !== 'http://localhost:8080') {
    	return
        }
        console.log('http://localhost:8080/index.jsp ===> http://localhost:8090/a.ftl')
        // 打印数据
        console.log(e)
        // 回传数据
        e.source.postMessage('hello wujiang', e.origin)
    }, false)
    } else if (window.attachEvent) {
    window.attachEvent('onmessage', function (e) {
        if (e.origin !== 'http://localhost:8080') {
    	return
        }
        console.log('http://localhost:8080/index.jsp ===> http://localhost:8090/a.ftl')
        // 打印数据
        console.log(e)
        // 回传数据
        e.source.postMessage('hello wujiang', e.origin)
    }, false)
    }
    </script>
    
    • data:发送的数据
    • origin:源IP地址
    • source:源窗口,可以通过source返回数据到源窗口

    一个页面打开另一个页面

    // 打开一个新窗口
    var newWin = window.open('http://localhost:8090/a');
    // 向新窗口发送数据
    setTimeout(function () {
        console.log('http://localhost:8080/a.jsp ===> http://localhost:8090/a.ftl');
        newWin.postMessage({name: 'wujiang'}, 'http://localhost:8090');
    }, 1000);