iframe 首要的区分就是 是否同源,我就以同源和不同源两种情况去分析,页面和iframe 之间的交互和通信。
调试方式说明
为了方便本地调试,我使用了Vscode的插件 live Server,来启动本地的静态服务。此静态服务包含
协议+ip+端口;例如:
http://127.0.0.1:56741/2.html然后不同服务可以理解为不同源的页面,同一服务可以理解为同源的页面,以此来方便测试。
当然除了 live Server这种方案,使用webpack等依赖实现静态服务也是OK的,有能力可自行实现
1.html
DOCTYPE html>
<html lang="en">
<head>
<style>
.iframe1 {
background: pink;
}
style>
head>
<body>
我是主页面1.html,主页面引入一个同源的 2.html
<button id="btn">点我弹出消息button>
<br>
<iframe src="http://127.0.0.1:56741/2.html" class="iframe1" id="iframe1">iframe>
<script>
window.onload = function () {
var iframe1 = document.getElementById('iframe1')
console.log(iframe1)
//
console.log(iframe1.contentDocument)
var info = iframe1.contentDocument.getElementById('info')
console.log(info)
// 大家好,我是2.html页面
info.style.color = 'yellow'
// 是可以修改iframe中 id为info元素的文本颜色的
}
script>
body>
html>
2.html
DOCTYPE html>
<html lang="en">
<head>
head>
<body>
<h1 id="info">大家好,我是2.html页面h1>
<script>
window.onload = function () {
// 借助 window.parent,可以操作父页面。例如我这里给父页面的按钮添加一个点击事件
var btn = window.parent.document.getElementById('btn')
btn.addEventListener('click', function () {
alert('子页面给父页面添加点击事件')
})
}
script>
body>
html>

# 两个页面的地址
http://127.0.0.1:56741/1.html
http://127.0.0.1:56741/2.html
# 做了那些操作
1. 获取dom元素,修改颜色,添加事件
能够获取dom,那么就代表我们可以随意操作页面
# 父 操作 子iframe
iframe.contentDocument
# 子iframe 操作 父
window.parent.document
其他注意事项:
注意一下,需要添加一个 window.onload事件,切记
通信最常见的通信就是 postmessage,写个示例:
父传子
<body>
我是主页面1.html,主页面引入一个同源的 2.html
<br>
<iframe src="http://127.0.0.1:56741/2.html" class="iframe1" id="iframe1">iframe>
<script>
const msg = {
name: "A"
}
window.onload = () => {
// 自动调用必须放在onload中,通过事件调用则不用
let frame = document.querySelector("#iframe1").contentWindow
frame.postMessage("父传子--番茄", "http://127.0.0.1:56741")
}
script>
body>
<body>
<h1 id="info">大家好,我是2.html页面h1>
<script>
window.onload = function () {
window.addEventListener("message", (e) => {
console.log(e.data)
// 父传子--番茄
})
}
script>
body>
子传递父
<body>
我是主页面1.html,主页面引入一个同源的 2.html
<br>
<iframe src="http://127.0.0.1:56741/2.html" class="iframe1" id="iframe1">iframe>
<script>
window.onload = function () {
// 给window加一个监听事件,监听message
window.addEventListener('message', (e) => {
console.log('message', e)
})
}
script>
body>
<body>
<h1 id="info">大家好,我是2.html页面h1>
<button id="sonBtn">点我给父页面发消息button>
<script>
window.onload = function () {
document.getElementById('sonBtn').onclick = function () {
console.log('开始发送')
// 1.通过 contentWindow,发送消息`我是小番茄` 给 http://127.0.0.1:56741
window.top.postMessage('我是小番茄', 'http://127.0.0.1:56741')
}
}
script>
body>
父页面获取子页面
<body>
我是主页面1.html,
http://127.0.0.1:56741/1.html
<button id="btn">点我弹出消息button>
<br>
<iframe src="http://127.0.0.1:58056/3.html" class="iframe3" id="iframe3">iframe>
<script>
window.onload = function () {
var iframe3 = document.getElementById('iframe3')
console.log(iframe3)
//
console.log(iframe3.contentDocument)
// null
}
script>
body>
子页面获取父页面
<body>
<h1 id="h222">大家好,我是不同源的页面3h1>
http://127.0.0.1:58056/3.html
<script>
window.onload = function () {
console.log(window.parent.document)
/*
3.html:16 Uncaught DOMException: Blocked a frame with origin "http://127.0.0.1:58056" from accessing a cross-origin frame.
at window.onload (http://127.0.0.1:58056/3.html:16:33)
*/
}
script>
body>
<body>
我是主页面1.html(http://127.0.0.1:56741),主页面引入一个不同源的 3.html
<br>
<iframe src="http://127.0.0.1:58056/3.html" class="iframe3" id="iframe3">iframe>
<script>
window.onload = () => {
// 自动调用必须放在onload中,通过事件调用则不用
let frame = document.querySelector("#iframe3").contentWindow
frame.postMessage("父传子--番茄333", "http://127.0.0.1:58056 ")
}
script>
body>
<body>
<h1 id="info">大家好,我是2.html页面h1>
<script>
window.onload = function () {
window.addEventListener("message", (e) => {
console.log(e.data)
// 父传子--番茄333
})
}
script>
body>
答:可以,例如按钮之类的页面完全是允许自由操作的。
答:例如:父页面 A 中有一个 iframe 指向同源的 B 页面,A页面可以获取到 B页面的dom。但是当 B 页面利用例如 location.href = 'https://www.baidu.com' 跳转到百度等页面,在A页面就无法获取B页面的dom了。
兼容性考虑:
https://caniuse.com/?search=postMessage

综上所述:
最优解是同源的情况,可以直接操作 dom,可以不借助 postMessage 直接通信。
如果不同源,可以通过 postMessage,传递消息。但是需要在 iframe 中添加代码主动发送消息,外层页面监听 message 事件接受数据;