【问题标题】:How to set iframe content of a react component如何设置反应组件的 iframe 内容
【发布时间】:2016-04-17 01:04:14
【问题描述】:

我正在尝试在 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;

我做错了什么?

【问题讨论】:

  • 您是打算将文档加载到 IFrame 元素中还是要自己设置/组合 IFrame 内容?
  • 嗨 Lukas,我必须自己设置内容。在我的情况下,我对服务器进行 ajax 调用以取回一些数据。这个“somedata”有一个我需要设置到 iframe 中的脚本。

标签: javascript iframe reactjs xmlhttprequest


【解决方案1】:

TLDR;

如果您正在寻找一种通过 React 以事实上的规范方式控制 &lt;iframe&gt; 内容的方法,Portals 是您的最佳选择。 与 Portal 的所有事物一样:一旦您建立对现有和已安装的 DOM 节点的引用(在这种情况下,这将是给定 &lt;iframe&gt;contentWindow)和 用它创建一个 Portal,它的内容也被认为是 «parent» 虚拟 DOM 的子级,这意味着共享(合成)事件系统、上下文等。

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

示例:一个功能性 React 组件,包括 hooks

// 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>
)

进一步的控制,例如对 &lt;iframe&gt;s &lt;head&gt; 内容的控制,可以很容易地实现为 this Gist 显示。

还有react-frame-component,imho 提供的一个包非常多 在 React 中使用受控 &lt;iframe&gt;s 时所需的一切。

注意事项:

  • 此答案仅针对使用案例,其中给定 &lt;iframe&gt; 的所有者希望以 React 方式以编程方式控制(如决定)其内容。
  • 此答案假定&lt;iframe&gt; 的所有者遵守Same-origin policy
  • 此答案不适用于跟踪在 &lt;iframe src="https://www.openpgp.org/&gt; 类型的场景中加载外部资源的方式和时间。
  • 如果您关心可访问性,您应该为您的 iframe 提供meaningful title attributes

用例(我知道的);

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

将一组给定的 CSS 样式(或样式表)添加到受控的 &lt;iframe&gt;

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

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

然而,在当今时代,尤其是在 React 世界中,大多数时候,构建设置会动态创建样式和样式表: 因为他们利用了像 SASS 这样的元语言,或者像 CSS-in-JS 这样的更复杂的解决方案(styled-componentsemotion)。

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

这个答案曾经也给出了关于 16.3 之前的 React 版本的食谱。然而,在这个时间点上,我认为可以肯定地说,大多数 我们能够推出一个 React 版本,包括 Portals,以及在较小程度上的 hooks。如果您需要有关 iframe 和 React 版本

【讨论】:

  • @Sephyre:该示例与 material-ui 本身无关,仅用于说明如何将样式从父窗口复制到嵌套窗口。恐怕真的不能复制粘贴。 material-ui 依赖于 JSS,并且有自己的一套关于样式注入方式和位置的规则。他们的这部分文档应该为您提供一些解决问题的建议:material-ui.com/styles/advanced/#insertionpoint
  • @RichardZilahi 那是复制粘贴留下的,对不起。我用代码框替换了这个例子,希望这段代码比我之前半途而废的尝试更有意义。
  • @GWorking 感谢您的澄清。我的猜测是这些库使用某种注入机制将样式和字体添加到 DOM。在 iframe 中,通过这种方法,他们只会将资产注入顶级文档。如果您能以某种方式识别和选择所述资产,则可以使用代码框示例中的“克隆链接”方法。
  • @Tom 一般来说,你是绝对正确的。然而,在 iframe 的情况下,我们必须等待相关的 DOM 元素被挂载才能渲染其子元素,而其他任何 DOM 元素都不是这种情况。因此,上线的 DOM 元素实际上是我们想要对其做出反应的状态变化。因此useState.
  • @ImanMarashi postMessage 是一个 Window 方法,我们在派生 mountNode 时已经访问了与 iframe 相关的实例(参见第一个示例)。基本上contentRef?.contentWindow?.postMessage()
【解决方案2】:

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

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

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

还有易于使用的react-frame-component 包,在接受的答案中提到。

【讨论】:

    【解决方案3】:

    这也有效(IE 不支持)。

    const myHTML = <h1>Hello World</h1>
    <iframe srcDoc={myHTML} />
    

    更多信息在这里:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe

    【讨论】:

      【解决方案4】:

      您可以使用 iframe 的srcdoc 属性。它会起作用的!

      srcdoc:要嵌入的内联 HTML,覆盖 src 属性。

      阅读:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe

      【讨论】:

        【解决方案5】:

        使用 DOMParser constructor's parseFromString 解析 html 比公认的答案要简单一些。这是一个从 DOMParser 生成的文档中检索已解析的 html 的示例。如果您要向 iframe 发送元素,请省略 parseHtml.body.innerText 部分。

        class SimpleIframe extends Component {
            render() {
                const parseHtml = html => new DOMParser().parseFromString(html, 'text/html').body.innerText;
                return <iframe srcDoc={parseHtml(this.props.wholeHTMLDocumentString)} />;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-04
          • 2013-05-06
          • 1970-01-01
          • 2014-10-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多