跳到主要内容

iframe 在同源和跨域的通信方式

· 阅读需 4 分钟
素明诚
Full stack development

同源

父页面和 iframe 可以直接访问对方的 DOM 和 JavaScript 对象。

这种方式简单高效,但需要注意代码的安全性和健壮性,避免产生错误或安全漏洞。

跨域

由于同源策略的限制,需要使用 window.postMessage() 方法进行通信。

同源情况下的通信

直接访问 iframe 的内容

从父页面访问 iframe

父页面可以通过 iframe 元素的 contentWindowcontentDocument 属性获取 iframewindow 对象或 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;
}