iframe通信

238 阅读3分钟

由于项目存在,需要内嵌iframe,使其能够实现动态展示广告,因此需要对其通信的方法做个汇总。既然是iframe,就会存在同域和跨域的情况。现在分别针对不同情况下的iframe,做互相通信。

同域

  • 原理:因为同个域名下的,dom是允许调用的。
  • 调用方法:
父页面调用子页面方法:FrameName.window.childMethod() 或者 document.getElementById("child").contentWindow.childMethod()
父页面访问子页面dom:FrameName.window.document.getElementById("text").innerText="2"
子页面调用父页面方法:parent.window.parentMethod();
子页面调用父页面dom:parent.window.document.getElementById("text").innerText="1"
  • 案列
父页面:
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        function say() {
            alert("我是父页面")
        }
        function childMethod() {
            document.getElementById("child").contentWindow.say()//调用子页面的方法
            document.getElementById("child").contentWindow.document.getElementById("text").innerText="父页面更改子页面"
            // child.window.say()
            // child.window.document.getElementById("text").innerText="父页面更改子页面"
        }
    </script>
</head>
<body>
    父页面
    <div style="width: 100px;height: 100px; background: red;" id="text"></div>
    <div style="width: 100px;height: 100px; background: yellow;" onclick="childMethod()"></div>
    <iframe id="child" name="child" src="./iframechild.html" frameborder="0" width="500" height="500"></iframe>
</body>
</html>
子页面:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        function say() {
            alert("我是子页面")
        }
        function parentMethod() {
            parent.window.say()//调用父页面的方法
            parent.window.document.getElementById("text").innerText="子页面更改父页面"
        }
    </script>
</head>
<body>
    子页面
    <div style="width: 100px;height: 100px; background: red;" id="text"></div>
    <div style="width: 100px;height: 100px; background: yellow;" id="parentMethod" onclick="parentMethod()"></div>
</body>
</html>

跨域

  • 实现方法:
  1. 可以使用postMessage通信
  2. 使用链接描点及定时器实现

postMessage

  • 原理:window.postMessage() 方法提供了一种受控机制来规避非同源限制,一个窗口可以获得对另一个窗口的引用(比如 targetWindow = window.opener),然后在窗口上调用 targetWindow.postMessage() 方法分发一个  MessageEvent 消息。接收消息的窗口可以根据需要自由处理此事件 (en-US)。传递给 window.postMessage() 的参数(比如 message )将通过消息事件对象暴露给接收消息的窗口
  • 调用方法:
发送消息:
otherWindow.postMessage(message, targetOrigin, [transfer]);
`otherWindow`:其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的[window.frames]
`message`:将要发送到其他 window的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。
`targetOrigin`:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的origin属性完全一致,来防止密码被恶意的第三方截获。**如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
接收消息:
        window.addEventListener("message", function(event) {   
                console.log(event.data);
            }, false);
  • 案列:父页面和子页面都是不同域名下的
父页面:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        window.onload=function () {
            child.postMessage("您好","*")
        }
        window.addEventListener("message", function(event) {   
            if (event.data==="已经到消息") {
                alert("父类收到子类消息:"+event.data);
            }      
            }, false);
    </script>
</head>
<body>
    父页面
    <iframe id="child" name="child" src="http://localhost:3000/iframechild.html" frameborder="0" width="500" height="500"></iframe>
</body>
</html>
子页面:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        window.addEventListener("message", function(event) {         
                alert("子类收到父类消息:"+event.data);
                parent.postMessage("已经到消息","*")
            }, false);
    </script>
</head>
<body>
    子页面
</body>
</html>

使用链接描点及定时器实现

  • 原理:使用地址栏链接,通过更改描点,页面不会刷新和跳转。通过描点的变化,来传递信息。
  • 案列:父页面和子页面都是不同域名下的
父页面:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        var oldtext;
        var iframeText = function() {    
        var hash = window.location.hash.slice(1), text;
        if (hash && /text=/.test(hash)) {
            text = hash.replace("text=", "");
            if (oldtext!==text) {
                console.log("父页面收到:"+text);
                oldtext=text
                var a=('http://localhost:3000/iframechild.html#text='+(parseInt(text)+1) )
                document.getElementById("child").src=a
            }

        }
    setTimeout(iframeText, 10000);
};
window.onload=function(){
            iframeText()
        }
    </script>
</head>
<body>
    父页面
    <iframe id="child" name="child" src="http://localhost:3000/iframechild.html" frameborder="0" width="500" height="500"></iframe>
</body>
</html>
子页面:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        var oldtext=1
        var iframeText = function() {    
            var hash = window.location.hash.slice(1), text=oldtext;
            if (hash && /text=/.test(hash)) {
                text = hash.replace("text=", "");
                if (oldtext!==text) {
                    oldtext=text
                    console.log("子页面收到:"+text);
                    parentMethod()
                }

            }
            setTimeout(iframeText, 10000);
        }
        window.onload=function(){
            iframeText()
        }

        function parentMethod() {
            window.top.location="http://127.0.0.1:5500/iframeparent.html#text="+oldtext
        }
    </script>
</head>
<body>
    子页面
    <div style="width: 100px;height: 100px; background: yellow;" id="parentMethod" onclick="parentMethod()"></div>
</body>
</html>