如何设置 React 组件的 iframe 内容

新手上路,请多包涵

我正在尝试在 React 组件中设置 iframe 的内容,但我做不到。我有一个组件,其中包含一个函数,当 iframe 完成加载时必须调用该函数。在该函数中,我正在设置内容,但似乎根本没有调用 onload 函数。我正在 Chrome 浏览器中测试它。我正在尝试以下操作:

 var MyIframe = React.createClass({
    componentDidMount : function(){
        var iframe = this.refs.iframe.getDOMNode();
        if(iframe.attachEvent){
            iframe.attacheEvent("onload", this.props.onLoad);
        }else{
            iframe.onload = this.props.onLoad;
        }
    },
    render: function(){
        return <iframe ref="iframe" {...this.props}/>;
    }
});

var Display = React.createClass({
    getInitialState : function(){
        return {
            oasData : ""
        };
    },
    iframeOnLoad : function(){
        var iframe = this.refs.bannerIframe;
        iframe.contentDocument.open();
        iframe.contentDocument.write(['<head></head><style>body {margin: 0; overflow: hidden;display:inline-block;} html{ margin: 0 auto; text-align: center;} body > a > img {max-width: 100%; height: inherit;}', extraCss, '</style></head><body>', this.state.oasData.Ad[0].Text, '</body>'].join(''));
        iframe.contentDocument.close();
    },
    setOasData : function(data){
        this.setState({
            oasData : JSON.parse(data)
        });
    },
    componentDidMount : function(){
        var url = "getJsonDataUrl";

        var xhttp = new XMLHttpRequest();
        var changeOasDataFunction = this.setOasData;
        xhttp.onreadystatechange = function () {
            if (xhttp.readyState == 4 && xhttp.status == 200) {
                changeOasDataFunction(xhttp.responseText);
            }
        };
        xhttp.open("GET", url, true);
        xhttp.send();
    },
    render : function(){
        return (
            <MyIframe refs="bannerIframe" onLoad={this.iframeOnLoad} />
        );
    }
});

module.exports = Display;

我究竟做错了什么?

原文由 user3807940 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2k
2 个回答

TLDR;

编辑 react-iframe-examples

如果您正在寻找一种方法来以事实上的规范方式通过 React 控制 <iframe> 的内容,那么 门户网站 就是您的不二之选。与 Portal 的所有事物一样:一旦您建立对现有和已安装 DOM 节点的引用(在本例中,它将是给定的 contentWindow <iframe> )并使用它创建一个 Portal,其内容也是被认为是 «parent» 虚拟 DOM 的孩子,这意味着共享(合成)事件系统、上下文等。

请注意,为了代码简洁,下面的示例使用了 Optional chaining operator ,在撰写本文时,并非所有浏览器都支持它。

示例:一个包含 钩子 的 _功能性 React 组件_:

 // iframe.js

import React, { useState } from 'react'
import { createPortal } from 'react-dom'

export const IFrame = ({
  children,
  ...props
}) => {
  const [contentRef, setContentRef] = useState(null)
  const mountNode =
    contentRef?.contentWindow?.document?.body

  return (
    <iframe {...props} ref={setContentRef}>
      {mountNode && createPortal(children, mountNode)}
    </iframe>
  )
}

示例:一个 _React 类组件_:

 // iframe.js

import React, { Component } from 'react'
import { createPortal } from 'react-dom'

export class IFrame extends Component {
  constructor(props) {
    super(props)
    this.state = {
      mountNode: null
    }
    this.setContentRef = (contentRef) => {
      this.setState({
        mountNode: contentRef?.contentWindow?.document?.body
      })
    }
  }

  render() {
    const { children, ...props } = this.props
    const { mountNode } = this.state
    return (
      <iframe
        {...props}
        ref={this.setContentRef}
      >
        {mountNode && createPortal(children, mountNode)}
      </iframe>
    )
  }
}

用法:

 import { IFrame } from './iframe'

const MyComp = () => (
    <IFrame>
        <h1>Hello Content!</h1>
    </IFrame>
)

进一步的控制,例如 <iframe> s <head> 内容,可以很容易地实现,如 本要点 所示。

还有 react-frame-component ,恕我直言,这个包提供了在 React 中使用受控 <iframe> 时所需的几乎所有内容。

注意事项:

  • 该答案仅解决用例,其中给定 <iframe> 的所有者希望以 React-ish 方式以编程方式控制(如决定)其内容。
  • 该答案假定 <iframe> 的所有者遵守 同源政策
  • 这个答案不适合跟踪在 <iframe src="https://www.openpgp.org/> 种场景中加载外部资源的方式和时间。
  • 如果您关心可访问性,则应该为 iframe 提供 有意义的标题属性

用例(我知道的);

  • OP 的用例:广告以及控制广告如何以及何时可以访问您网站上安全范围内的元素的需要。
  • 可嵌入的第三方小部件。
  • 我的用例(以及我对此事的一些知情立场):CMS UI,您希望用户能够在其中预览范围内的 CSS 样式,包括应用的媒体查询。

将一组给定的 CSS 样式(或样式表)添加到受控的 <iframe>

正如一位评论作者指出的那样,管理父应用程序和受控内容之间的样式 <iframe> 可能非常棘手。如果您足够幸运,拥有 (a) 专用 CSS 文件,其中包含所有必要的视觉说明,适用于您的 <iframe> ,只需传递您的 IFrame 组件 a <link> 标签引用所述样式,即使这不是最符合标准的方式 <link> 参考:

 const MyComp = () => (
  <Frame>
    <link rel="stylesheet" href="my-bundle.css">
    <h1>Hello Content!</h1>
  </Frame>
)

然而,在当今时代,尤其是在 React 世界中,大多数时候,构建设置会即时创建样式和样式表:因为它们利用了像 SASS 这样的元语言,或者更复杂的解决方案,如 CSS-in-JS 之类的东西( 样式组件情感)。

这个沙盒包含如何将一些更流行的样式策略与 React 中的 iframe 集成的示例。

编辑 react-iframe-examples

这个答案过去也提供了关于 16.3 之前的 React 版本的食谱。然而,在这个时间点,我认为可以肯定地说,我们大多数人都能够完成一个包含 Portals 的 React 版本,并且在较小程度上,还有 hooks。如果您需要有关 iframe 和 React 版本 < 16 的解决方案,请联系我,我很乐意提供建议。

原文由 Lukas Bünger 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果有人只想在 iframe 中显示小的 HTML,则有一个更简单的解决方案。

 <iframe src={"data:text/html,"+encodeURIComponent(content)}/>

内容的最大长度为 32768 个字符。

在接受的答案中也提到了易于使用 的 react-frame-component 包。

原文由 Jakub Zawiślak 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题