start
- 任务需要:希望父页面中嵌入 iframe 页面,但是在此之前对 iframe了解不够深入,让我们今天来研究一下 iframe ;
思路
iframe 首要的区分就是 是否同源,我就以同源和不同源两种情况去分析,页面和iframe 之间的交互和通信。
1. 同源的iframe
调试方式说明
为了方便本地调试,我使用了Vscode的插件 live Server,来启动本地的静态服务。此静态服务包含
协议+ip+端口
;例如:
http://127.0.0.1:56741/2.html
然后不同服务可以理解为不同源的页面,同一服务可以理解为同源的页面,以此来方便测试。
当然除了 live Server这种方案,使用webpack等依赖实现静态服务也是OK的,有能力可自行实现
1.1 正常交互
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)
// <iframe src="http://127.0.0.1:58056/3.html" class="box3" id="demo3"></iframe>
console.log(iframe1.contentDocument)
var info = iframe1.contentDocument.getElementById('info')
console.log(info)
// <h1 id="h222" style="color: yellow;">大家好,我是2.html页面</h1>
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>
总结
- 在同源的情况下 ,例如我演示的示例,父页面和嵌套的 iframe 是可以互相操作的。
# 两个页面的地址
http://127.0.0.1:56741/1.html
http://127.0.0.1:56741/2.html
# 做了那些操作
1. 获取dom元素,修改颜色,添加事件
能够获取dom,那么就代表我们可以随意操作页面
- 操作dom的方法
# 父 操作 子iframe
iframe.contentDocument
# 子iframe 操作 父
window.parent.document
-
其他注意事项:
注意一下,需要添加一个 window.onload事件,切记
1.2 通信
通信最常见的通信就是 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>
总结
- 主要是通过 window上的 postMessage 方法发送数据,另一个页面监听 message 事件即可;
- postMessage 参数主要有两个,一个是需要发送的信息(建议是字符串兼容性较好),一个是目标地址;
- 生产环境需要考虑安全性,在 message 事件中,需要判断消息来源的地址;
2. 不同源的iframe
2.1 正常交互
父页面获取子页面
<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)
// <iframe src="http://127.0.0.1:58056/3.html" class="box3" id="demo3"></iframe>
console.log(iframe3.contentDocument)
// null
}
</script>
</body>
子页面获取父页面
<body>
<h1 id="h222">大家好,我是不同源的页面3</h1>
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>
总结:
- 父页面无法操作子页面的dom,获取的时候直接返回 null;
- 子页面无法获取父页面,获取的时候直接报错;
2.2 通信
<!-- 父页面 -->
<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>
总结:
- postMessage可以跨域通信
3. 其他问题的思考
问题一:页面中的 iframe 的内容可以随意操作吗?
答:可以,例如按钮之类的页面完全是允许自由操作的。
问题二:页面中的 iframe 的页面跳转到其他页面会如何
答:例如:父页面 A 中有一个 iframe 指向同源的 B 页面,A页面可以获取到 B页面的dom。但是当 B 页面利用例如 location.href = 'https://www.baidu.com'
跳转到百度等页面,在A页面就无法获取B页面的dom了。
4.兼容性
兼容性考虑:
https://caniuse.com/?search=postMessage
总结:
综上所述:
-
最优解是同源的情况,可以直接操作 dom,可以不借助 postMessage 直接通信。
-
如果不同源,可以通过 postMessage,传递消息。但是需要在 iframe 中添加代码主动发送消息,外层页面监听 message 事件接受数据;