父页面和子页面(iframe)处于2个不同的进程(即使在同一个域),进程之间的通讯必须通过每个进程内【事件监听所在的线程】来完成。最重要的是,通讯时传输的数据格式必须是序列化的格式,序列化格式指【一维】【线性】的数据类型比如字符串、字节流等,在浏览器中有这么几种序列化格式可选:
下面介绍2种方法(可能性),可以让父页面以最大的自由度读写iframe的内容,这两种方法的优点是:子页面的开发商只需要一点点的配合就能实现,主动权掌握在父页面手中。缺点是:需要熟悉子页面的结构,本质上属于代码渗透,可能有性能影响。
有一种特殊情况下,不同的进程可以共享内存空间:如果父页面和iframe同域,问题就简单多了,也不必传输序列化数据了,父页面可以直接访问并修改iframe的全局作用域window对象(因为浏览器认为同域之间相互信任)。
比如想去掉iframe中的导航栏(nav元素),可以通过下面的代码实现:
iframe.contentDocument.querySelector('nav').remove();
如果只是想隐藏掉,可以这样:
iframe.contentDocument.querySelector('nav').style.display='none';
想点击某个按钮(button元素):
iframe.contentDocument.querySelector('button').click();
同理,iframe中也可以直接访问父页面的全局环境:
window.parent.document //document和DOM有关
如果不幸父页面和iframe不同域,那只能通过跨域的方式传序列化数据:父页面向子页面postMessage传值,子页面监听message事件。
比如希望传递一份CSS字符串,覆盖iframe中的样式:
iframe.contentWindow.postMessage({
type:
"css",
content:
`
body {
font-size: 25px !important;
} `
},
"*");
如果希望传一段js代码给iframe执行,可以这样:
iframe.contentWindow.postMessage({
type:
"js",
content:
`
alert('hello world') `
},
"*");
以上2种情况,iframe中可以这样监听:
window.addEventListener("message",
(event)
=>
{
if
(event.data.type ===
'css')
{
const style = document.createElement('style');
style.innerHTML =
event.data.content;
document.body.appendChild(style);
}
else
if
(event.data.type ===
'js')
{
eval(event.data.content);
}
},
false);
同理,iframe向父页面发送数据:
window.parent.postMessage()
<完>