同源 iframe 才能安全访问 contentdocument,跨域直接读取触发 securityerror;必须监听 load 事件后获取 doc = iframe.contentdocument || iframe.contentwindow.document,并校验 readystate 和节点存在性。

iframe.contentDocument 读取同源页面内容
只有同源(协议、域名、端口完全一致)的 iframe 才能安全访问其内部 DOM。跨域直接读取会触发 SecurityError,这是浏览器强制限制,无法绕过。
正确做法是先检查 iframe 是否加载完成,再通过 contentDocument 或 contentWindow.document 获取文档对象:
const iframe = document.getElementById('myIframe');
iframe.addEventListener('load', () => {
try {
const doc = iframe.contentDocument || iframe.contentWindow.document;
const title = doc.title;
const h1Text = doc.querySelector('h1')?.textContent;
console.log(title, h1Text);
} catch (e) {
console.error('无法访问 iframe 内容:', e.message); // 常见报错:Blocked a frame from accessing a cross-origin frame
}
});
注意:contentDocument 在大多数现代浏览器中可用,但 IE8–10 需 fallback 到 contentWindow.document;务必等 load 事件后操作,否则 contentDocument 可能为 null。
用 postMessage 实现跨域 iframe 通信
跨域场景下,不能读取对方 DOM,但可通过 postMessage 主动发起双向消息传递——这是唯一合规、稳定的方式。
父页向子页发消息(假设子页已知 origin):
const iframe = document.getElementById('myIframe');
iframe.contentWindow.postMessage({ type: 'GET_DATA' }, 'https://example.com'); // 必须指定目标 origin,不能用 *
子页监听并响应(需在 iframe 页面内运行):
window.addEventListener('message', (e) => {
if (e.origin !== 'https://parent.com') return; // 严格校验来源
if (e.data.type === 'GET_DATA') {
e.source.postMessage({
type: 'DATA_RESPONSE',
payload: { title: document.title, url: location.href }
}, e.origin);
}
});
父页再监听子页回复:
window.addEventListener('message', (e) => {
if (e.source !== iframe.contentWindow || e.origin !== 'https://example.com') return;
if (e.data.type === 'DATA_RESPONSE') {
console.log('收到子页数据:', e.data.payload);
}
});
关键点:postMessage 不是“读取”,而是“请求 + 回传”;e.source 和 e.origin 必须双重校验,否则存在 XSS 风险;消息体必须是可序列化的 plain object,不能传函数或 DOM 节点。
iframe 尚未加载完成就调用 contentDocument 的典型错误
常见现象:控制台报错 Cannot read property 'title' of null 或 contentDocument is null,本质是脚本执行早于 iframe 加载完毕。
避免方式包括:
- 监听
iframe元素的load事件(最可靠) - 不依赖
DOMContentLoaded或document.ready,它们不保证 iframe 已 ready - 若 iframe 是动态插入的,确保插入后才绑定
load监听器,且不要漏掉已缓存的情况(可检查iframe.contentDocument?.readyState === 'complete') - 服务端渲染(SSR)场景下,注意 hydration 时机,React/Vue 中需用
useEffect或mounted钩子延迟操作
为什么 getElementsByTagName('body')[0] 拿不到内容?
不是所有 iframe 文档都立即有 。尤其当 iframe 指向一个空 HTML 或仅含 的页面时,getElementsByTagName('body') 返回空数组,[0] 会是 undefined。
更稳妥的做法是:
- 先确认
doc.readyState === 'complete' - 用
doc.body(它在文档解析完成后始终存在,即使为空) - 如果要取文本内容,优先用
doc.documentElement.textContent,它包含整个文档树的文本,不依赖 body 是否渲染 - 避免链式调用如
doc.body.innerHTML前不判空,应写成doc.body?.innerHTML
DOM 访问的脆弱性往往不在跨域,而在时序和结构假设——哪怕同源,也要把 null 和 undefined 当作常态处理。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











