同源iframe直接读scrollheight常为0,因onload时css未渲染、图片未加载、js未执行完;须用settimeout(() => {}, 0)延后读documentelement.scrollheight(fallback body.scrollheight),并重置body{margin:0;padding:0},兜底math.max(height,300)。

同源 iframe 直接读取 scrollHeight 为什么经常取到 0
因为没等 DOM 渲染完成就去读 contentDocument.body.scrollHeight。常见写法是给 iframe 绑定 onload,但 onload 触发时,子页 CSS 可能还没应用、图片未加载、JS 还在执行——此时 scrollHeight 往往是 0 或远小于真实值。
- 必须在
iframe.onload后加setTimeout(() => { ... }, 0),让浏览器完成重排(reflow)后再读高度 - 优先用
document.documentElement.scrollHeight,fallback 到document.body.scrollHeight,避免 IE 和某些 SSR 场景下 body 为空的问题 - 子页
body必须重置:body { margin: 0; padding: 0; },否则默认 8px margin 会让高度多出一截 - 父页设置高度时建议兜底最小值:
ifr.style.height = Math.max(height, 300) + 'px',防内容为空时 iframe 塌缩
跨域 iframe 的 postMessage 高度同步必须双向校验 origin
只监听 message 事件却不校验 event.origin 和 event.source,等于把 iframe 的控制权白送给任意站点——这是典型的 XSS 入口。
- 父页接收消息时,必须同时检查:
e.source === ifr.contentWindow && e.origin === 'https://trusted.com' - 子页发送消息时,
targetOrigin参数严禁用'*',应明确写成'https://parent.example.com' - 子页发消息的时机不能依赖
DOMContentLoaded,而要用window.addEventListener('load', () => { ... }),确保图片、字体、异步脚本全部加载完 - 若子页是 Vue/React 应用,发消息逻辑要放在
mounted/useEffect(() => { ... }, [])中,避开 SSR 渲染阶段
内容动态变化时如何触发重新同步高度
静态页面 onload 一次就够了,但带加载更多、折叠展开、表单提交后刷新局部内容的 iframe,高度会变——这时候靠 onload 就失效了。
- 同源场景下,推荐在子页用
ResizeObserver监听document.body或根容器的高度变化,变化即发消息或更新父页样式 - 跨域场景下,子页自己无法监听父页行为,所以所有高度变更点(如按钮点击、AJAX 回调、路由切换)都得显式调用
window.parent.postMessage(...) - 父页不要假设“子页只会发一次”,要始终允许重复消息,并用最新值覆盖:
ifr.style.height = data.height + 'px',而不是 += - 避免在子页用
setInterval轮询高度——既浪费性能,又可能因跨域被拦截
height: 100% 在 iframe 上为什么永远不生效
这不是 bug,是 CSS 规范强制要求:百分比高度必须有可计算的包含块高度。而 html 和 body 默认是 auto,导致所有后代的 height: 100% 都退化为 0。
- 即使父容器设了
height: 100vh,iframe 内部仍需子页配合清除body默认边距,否则视觉上还是不对齐 - 别指望
display: flex或grid自动撑开 iframe——它只影响布局流,不改变 iframe 的渲染尺寸逻辑 - 真正满屏嵌入的唯一可靠路径是逐级声明:
html, body, .wrap, iframe { height: 100%; margin: 0; padding: 0; } - 但注意:这种写法和“自适应内容高度”目标冲突,二者不可兼得;选一个方向,别混用
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











