React使用createPortal向iframe渲染内容在Firefox中失效问题
问题原因分析
- 核心原因是不同浏览器对无src属性的iframe初始文档加载时机的实现差异:
- Edge/Chrome中,iframe元素挂载到DOM时会同步完成默认
about:blank文档的初始化,ref回调触发时即可拿到可用的body节点,因此createPortal可以正常挂载内容。 - Firefox中,iframe的初始文档加载是异步执行的,ref回调触发时
contentWindow.document尚未完成解析,此时获取到的body节点要么为空,要么处于不可写入的未就绪状态,挂载的portal内容不会被渲染。
- Edge/Chrome中,iframe元素挂载到DOM时会同步完成默认
- 额外排查点:如果你的iframe添加了
sandbox属性,需要确保配置了allow-same-origin,否则跨域限制会阻止父页面操作iframe内部的DOM节点。
修复方案
调整逻辑为监听iframe的load事件后再获取挂载节点,修改后的组件代码如下:
export class IFrame extends Component { constructor(props) { super(props) this.state = { mountNode: null } this.iframeRef = React.createRef() this.handleIframeLoad = () => { const iframe = this.iframeRef.current const body = iframe?.contentWindow?.document?.body if (body) { this.setState({ mountNode: body }) } } } render() { const { children, ...props } = this.props const { mountNode } = this.state return ( <> <iframe {...props} ref={this.iframeRef} onLoad={this.handleIframeLoad} /> {mountNode && createPortal(children, mountNode)} </> ) } }
补充说明:上述代码额外将createPortal移出了iframe标签内部,iframe的子元素仅作为不支持iframe场景的降级提示,和portal挂载逻辑无关,修改后可避免不必要的语义歧义。
内容的提问来源于stack exchange,提问作者emilevirus
相关产品推荐
相关产品推荐

