如何设置react组件的 iframe 内容

IT技术 javascript iframe reactjs xmlhttprequest
2021-04-28 23:41:26

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

我究竟做错了什么?

4个回答

TLDR;

编辑 react-iframe-examples

如果您正在寻找一种以<iframe>事实上的规范方式控制via React内容的方法,那么Portals是您的最佳选择。与 Portal 的所有事物一样:一旦您建立对现有和已安装 DOM 节点的引用(在这种情况下,这将是给定contentWindow<iframe>)并使用它创建一个 Portal,其内容也被视为“父”的子节点虚拟 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,一个包,imho 提供了<iframe>在 React 中使用受控s时所需的几乎所有东西

注意事项:

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

用例(我知道的);

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

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

正如一位评论作者所指出的,管理父应用程序和受控内容之间的样式<iframe>可能非常棘手。如果您有幸拥有 (a) 专用 CSS 文件,其中包含所有必要的可视化指令<iframe>,那么只需将引用所述样式IFrame<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 版本,以及在较小程度上,钩子。如果您需要有关 iframe 和 React 版本 <16 的解决方案,请联系我,我很乐意提供建议。

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

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

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

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

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

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

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

这也有效(IE 不支持)。

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

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