iframe在项目中经常会使用到,而且通常都存在跨域的情况,那么如何才能安全进行跨源通信呢?最常用的方法就是使用window.postMessage() 方法。
postMessage
window.postMessage() 方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为 https),端口号(443 为 https 的默认值),以及主机 (两个页面的模数 Document.domain
设置为相同的值) 时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。
以上摘自MDN 原文 postMessage;
1 2 3 4 5
| otherWindow.postMessage(message, targetOrigin, [transfer]);
|
postMessage十分强大,既可以子传父,也可以父传子,并且可以突破同源限制。
接下来我们来实际应用一下,想象一下这样一个场景,在主页面中有一个遮罩层,子页面是一个iframe内嵌的页面,子页面中有一个按钮,现在想要在点击子页面按钮的时候实现遮罩层的切换,该如何实现?这里我们就用到了postMessage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| // 子页面
<style> .btn{ width: 100px; height: 50px; margin: 60px; background-color: skyblue; border-radius: 10px; text-align: center; line-height: 50px; color: azure; cursor: pointer; }
.btn:hover { background-color:cornflowerblue; } </style> <div class="btn">点击按钮</div>
<script> const btn = document.querySelector('.btn') btn.addEventListener('click', function(event) { window.parent.postMessage('toggle', '*') }) window.addEventListener('message', function(event) { console.log(event.data.msg) const span = document.createElement('div') span.innerText = event.data.msg document.body.appendChild(span) }) </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| // 父页面
<style> .mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); } </style>
<div class="mask"> <iframe frameborder="0" style="width:800px; height: 300px; background-color: white;" src="http://blog.hpf0532.top/test.html" id="authIframe"></iframe> </div>
<script> const iframe = document.getElementById('son') const mask = document.querySelector('.mask') window.addEventListener('message', function (e) { console.log(e.data) if(e.data === 'toggle') { mask.classList.toggle('mask') iframe.contentWindow.postMessage({ msg: Date.now() }, 'http://blog.hpf0532.top') } } </script>
|
上面的代码其实很简单,在子页面里面获取了元素,该元素触发点击事件的时候,向父窗口发送一个消息,传递了一个消息(这个消息参数会在接收页面的e.data查到)。在父页面监听message事件,监听到了就让遮罩层切换。
完成切换后,父窗口也会向子页面发送一个消息。在子页面监听message事件,监听到了就会向子页面的DOM树中添加相应的元素。这样就实现了在跨域的情况下iframe页面之前的数据传递。