在 ReactJS 中将 iframe 高度设置为 scrollHeight

IT技术 javascript css reactjs iframe reactive-programming
2021-05-04 09:43:28
  • 与传统的静态 HTML 不同,由于其动态生成的组件结构和事件模型,该问题的典型解决方案在 React 中不起作用:

脚本:

<script>
  function resizeIframe(obj) {
    obj.style.height = obj.contentWindow.document.body.scrollHeight + 'px';
  }
</script>

html:

<iframe src="..." frameborder="0" scrolling="no" onload="resizeIframe(this)" />
  • 有一个 npm 包react-iframe,但它看起来未完成(仅接受 props url, width, height):

    https://www.npmjs.com/package/react-iframe

  • 解决方案的可能部分是侦听 的load事件iframe,但以与 React 兼容的方式。

React 中有没有办法将 aniframe的高度设置为其可滚动内容的高度?

我的代码:

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Iframe from 'react-iframe'

export default class FullheightIframe extends Component {

    componentDidMount() {
        console.log("IFRAME DID MOUNT");
    }

    renderReactFrame() {
        return (
            <Iframe url="http://www.example.com" width="100%" height="100%" onLoad={()=>{console.log("IFRAME ON LOAD")}}></Iframe>
        );
    }

    renderHTMLFrame() {
        return (
            <iframe 
                onLoad={(loadEvent)=>{
                    // NOT WORKING var frameBody = ReactDOM.findDOMNode(this).contentDocument.body; // contentDocument undefined
                    // NOT WORKING obj.nativeEvent.contentWindow.document.body.scrollHeight // contentWindow undefined
                }} 
                ref="iframe" 
                src="http://www.example.com" 
                width="100%" 
                height="100%" 
                scrolling="no" 
                frameBorder="0"
            />
        );
    }

    render() {
        return (
            <div style={{maxWidth:640, width:'100%', height:'100%', overflow:'auto'}}>
                {this.renderHTMLFrame()}
            </div>
        );
    }
}
4个回答

这是答案,但前两个重要的事情。

  • I帧必须在根组件render()方法
  • 必须从onLoad事件中捕获高度(如果完全加载 iframe)

这是完整的代码:

import React, { Component, PropTypes } from 'react'
import ReactDOM from 'react-dom'

export default class FullheightIframe extends Component {

    constructor() {
        super();
        this.state = {
            iFrameHeight: '0px'
        }
    }

    render() {
        return (
            <iframe 
                style={{maxWidth:640, width:'100%', height:this.state.iFrameHeight, overflow:'visible'}}
                onLoad={() => {
                    const obj = ReactDOM.findDOMNode(this);
                    this.setState({
                        "iFrameHeight":  obj.contentWindow.document.body.scrollHeight + 'px'
                    });
                }} 
                ref="iframe" 
                src="http://www.example.com" 
                width="100%" 
                height={this.state.iFrameHeight} 
                scrolling="no" 
                frameBorder="0"
            />
        );
    }
}

这里有几点需要注意:

  • 您可以使用 refs 来获取对 iframe 的引用,而不必搜索它
  • 使用 iframe 中的 onLoad() 处理程序来确保在您尝试调整内容大小之前已加载内容 - 如果您尝试使用 React 的生命周期方法,就像componentDidMount()您冒着内容不存在的风险一样
  • 您可能还需要一个调整大小的处理程序来确保 iframe 根据需要调整大小 - 只需确保在组件卸载时将其清理干净。
  • 您必须注意不同浏览器如何报告高度。寻找你能找到的最大的。
  • 如果 iframe 内容与您的代码位于不同的域中,您可能会遇到问题。有一些解决方案,例如react-iframe-resizer-super试图以跨域兼容的方式解决这个问题。

class WrappedFrame extends React.Component {
  state = { contentHeight: 100 };

  handleResize = () => {
    const { body, documentElement } = this.container.contentWindow.document;
    const contentHeight = Math.max(
      body.clientHeight,
      body.offsetHeight,
      body.scrollHeight,
      documentElement.clientHeight,
      documentElement.offsetHeight,
      documentElement.scrollHeight
    );
    if (contentHeight !== this.state.contentHeight) this.setState({ contentHeight });
  };
  
  onLoad = () => {
    this.container.contentWindow.addEventListener('resize', this.handleResize);
    this.handleResize();
  }
  
  componentWillUnmount() {
    this.container.contentWindow.removeEventListener('resize', this.handleResize);
  }
  
  render() {
    const { contentHeight } = this.state;
    return (
      <iframe
        frameBorder="0"
        onLoad={this.onLoad}
        ref={(container) => { this.container = container; }}
        scrolling="no"
        src="your.source"
        style={{ width: '100%', height: `${contentHeight}px` }}
        title="Some Content"
      />
    );
  }
}

在这个例子中,我们将确定的内容高度存储在组件的状态中,并使用该状态来设置呈现的 iframe 的高度。此外,通过将onLoad()处理程序定义放在组件中,您render()无需在每次重新渲染时创建新的处理程序函数,从而节省一点性能

你要做的是在你的componentDidMount中,运行脚本来设置高度。[如果您正在加载外部内容,您可能需要在 IFrame 上添加事件侦听器以等待外部内容加载完毕。]

   componentDidMount() {
       const obj = ReactDOM.findDOMNode(this);
       obj.style.height = obj.contentWindow.document.body.scrollHeight + 'px';
    }

还有另一种更“react”的方式来做到这一点 - 您可以将高度存储在状态中。

   componentDidMount() {
       const obj = ReactDOM.findDOMNode(this);
       this.setState({iFrameHeight:  obj.contentWindow.document.body.scrollHeight + 'px'});
    }

然后在你的渲染中:

render() {
    return (
        <div style={{maxWidth:640, width:'100%', height:this.state.iFrameHeight, overflow:'auto'}}>
            {this.renderHTMLFrame()}
        </div>
    );
}

到目前为止提出的答案都没有对我有用。至少在我的情况下,setTimeout从内部做短片的黑客方法onLoad似乎可以完成这项工作。

class SmartIFrame extends React.Component {
    render() {
        return <iframe srcDoc={this.props.srcDoc}
                       scrolling="no"
                       frameBorder={0}
                       width="100%"
                       onLoad = {e => setTimeout(() => {
                           const obj = ReactDOM.findDOMNode(this);
                           obj.style.height = obj.contentWindow.document.body.scrollHeight + 'px';
                       }, 50)}/>
    }
}