iframe 在同源和跨域的通信方式
· 阅读需 4 分钟
同源
父页面和 iframe 可以直接访问对方的 DOM 和 JavaScript 对象。
这种方式简单高效,但需要注意代码的安全性和健壮性,避免产生错误或安全漏洞。
跨域
由于同源策略的限制,需要使用 window.postMessage()
方法进行通信。
同源情况下的通信
直接访问 iframe 的内容
从父页面访问 iframe
父页面可以通过 iframe 元素的 contentWindow
或 contentDocument
属性获取 iframe 的 window
对象或 document
对象,然后直接访问其 DOM 或 JavaScript 变量。
从 iframe 访问父页面
iframe 可以通过 window.parent
访问父页面的 window
对象。
示例
父页面(parent.html)
<!DOCTYPE html>
<html>
<head>
<title>父页面</title>
</head>
<body>
<h1>父页面</h1>
<iframe id="myIframe" src="iframe.html"></iframe>
<script>
// 等待 iframe 加载完成
document.getElementById('myIframe').onload = function() {
// 获取 iframe 的 window 对象
var iframeWindow = document.getElementById('myIframe').contentWindow;
// 调用 iframe 中的函数
iframeWindow.showMessageFromIframe();
// 访问 iframe 中的变量
console.log('从 iframe 获取的变量:', iframeWindow.iframeVariable);
// 修改 iframe 中的 DOM 元素
var iframeDocument = iframeWindow.document;
iframeDocument.getElementById('iframeElement').innerText = '父页面修改了 iframe 的内容';
// 从 iframe 获取输入框的值
var inputValue = iframeDocument.getElementById('iframeInput').value;
console.log('从 iframe 获取的输入框值:', inputValue);
};
// 定义供 iframe 调用的函数
function showMessageFromParent() {
alert('这是父页面的消息!');
}
</script>
</body>
</html>
iframe 页面(iframe.html)
<!DOCTYPE html>
<html>
<head>
<title>iframe 页面</title>
</head>
<body>
<h2>iframe 页面</h2>
<p id="iframeElement">这是 iframe 中的段落。</p>
<input type="text" id="iframeInput" value="iframe 输入框的默认值">
<script>
// 定义变量
var iframeVariable = '这是 iframe 中的变量';
// 定义函数
function showMessageFromIframe() {
alert('这是 iframe 中的消息!');
}
// 调用父页面的函数
window.parent.showMessageFromParent();
// 访问父页面的 DOM 元素
var parentTitle = window.parent.document.title;
console.log('父页面的标题是:', parentTitle);
// 修改父页面的背景颜色
window.parent.document.body.style.backgroundColor = '#f0f8ff';
</script>
</body>
</html>
确保 iframe 已经加载完成,才能访问其内容。
跨域情况下的通信
当父页面和 iframe 来自不同的域名、协议或端口时,浏览器的同源策略将阻止它们直接访问对方的内容。
使用 postMessage 进行跨域通信
window.postMessage()
方法允许来自不同源的窗口之间安全地进行通信。
示例
父页面(parent.html)
<!DOCTYPE html>
<html>
<head>
<title>父页面</title>
</head>
<body>
<h1>父页面</h1>
<iframe id="myIframe" src="https://otherdomain.com/iframe.html"></iframe>
<script>
var iframe = document.getElementById('myIframe');
// 向 iframe 发送消息
iframe.onload = function() {
var message = { type: 'GREETING', text: '你好,iframe!' };
iframe.contentWindow.postMessage(message, 'https://otherdomain.com');
};
// 接收来自 iframe 的消息
window.addEventListener('message', function(event) {
// 验证消息来源
if (event.origin !== 'https://otherdomain.com') {
console.warn('来自未知来源的消息:', event.origin);
return;
}
// 处理消息
console.log('收到来自 iframe 的消息:', event.data);
});
</script>
</body>
</html>
iframe 页面(iframe.html)
<!DOCTYPE html>
<html>
<head>
<title>iframe 页面</title>
</head>
<body>
<h2>iframe 页面</h2>
<script>
// 接收来自父页面的消息
window.addEventListener('message', function(event) {
// 验证消息来源
if (event.origin !== 'https://parentdomain.com') {
console.warn('来自未知来源的消息:', event.origin);
return;
}
// 处理消息
console.log('收到来自父页面的消息:', event.data);
// 回复父页面
var reply = { type: 'RESPONSE', text: '你好,父页面!' };
event.source.postMessage(reply, event.origin);
});
</script>
</body>
</html>
在 postMessage
中指定准确的 targetOrigin
,不要使用 "*"
,以防止消息被发送到不受信任的窗口。
最佳实践和安全考虑
明确的 targetOrigin
:始终在 postMessage
中指定准确的目标源,防止数据泄露。
// 不推荐的做法
iframe.contentWindow.postMessage(message, '*'); // 可能导致安全风险
// 推荐的做法
iframe.contentWindow.postMessage(message, 'https://otherdomain.com');
验证 event.origin
:在接收消息时,验证消息的来源。
window.addEventListener('message', function(event) {
if (event.origin !== 'https://expected-origin.com') {
console.warn('不可信的消息来源,已忽略。');
return;
}
// 处理可信的消息
});
验证消息的数据结构和内容:确保消息的数据格式符合预期,防止被恶意代码利用。
if (typeof event.data !== 'object' || !event.data.type) {
console.warn('消息格式不正确,已忽略。');
return;
}