Ad

How To Set Iframe Content Of A React Component

- 1 answer

I am trying to set the content of an iframe in a React component but I am not able to do it. I have a component that contains a function which has to be called when the iframe finishes loading. In that function I am setting the content but it doesn't seem like the onload function is called at all. I am testing it in the Chrome browser. I am trying the following:

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;

What am I doing wrong?

Ad

Answer

TLDR;

Edit react-iframe-examples

If you're looking for a way to control the contents of an <iframe> via React in a de-facto canonical way, Portals are the way to go. And as with all things Portal: Once you establish a reference to an existing and mounted DOM node (in this case that would be the contentWindow of a given <iframe>) and create a Portal with it, its contents are also considered children of the «parent» virtual DOM, which means a shared (synthetic) event system, contexts and so on.

Please note that, for code brevity, the examples below make use of the Optional chaining operator, which as of this writing is not supported in all browsers.

Example: A functional React component including 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>
  )
}

Example: A React class component:

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

Usage:

import { IFrame } from './iframe'

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

Further control, for example over an <iframe>s <head> contents, can easily be achieved as this Gist shows.

There is also this Gist shows.

There is also react-frame-component, a package that imho offers pretty much everything you need when working with controlled <iframe>s in React.

Caveats:

  • This answer only addresses use cases, where the owner of a given <iframe> wants to programmatically control (as in deciding about) its contents in a React-ish way.
  • This answer assumes, that the owner of an <iframe> complies with the Same-origin policy.
  • This answer is not suited to track how and when external resources are loaded in an <iframe src="https://www.openpgp.org/> kind of scenario.
  • If accessibility is something you care about, you should give your iframes meaningful title attributes.

Use cases (that I know of);

  • The OP's use case: Ads and the need to control how and when those can access a safely scoped element on your website.
  • Embeddable third-party widgets.
  • My use case (and hence my somewhat informed stance on the matter): CMS UI's, where you want to enable users to preview scoped CSS styles, including applied media queries.

Adding a given set of CSS styles (or stylesheets) to a controlled <iframe>:

As one comment author pointed out, managing styles between a parent application and the contents of a controlled <iframe> can be quite tricky. If you're lucky enough to have (a) dedicated CSS file(s) incorporating all necessary visual instructions for your <iframe>, it might suffice to just pass your IFrame component a <link> tag referencing said style(s), even though this is not the most standard compliant way to go about <link>refs:

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

In this day and age, however, and especially in a React world, most of the time, build setups create styles and stylesheets on the fly: Because they leverage meta-languages like SASS or even more involved solutions like CSS-in-JS stuff (styled-components, emotion).

This sandbox contains examples of how to integrate some of the more popular styling strategies with iframes in React.

Edit react-iframe-examples

This answer used to also give recipes with regards to versions of React prior to 16.3. At this point in time, however, I think it's safe to say that most of us are able to pull off a React version including Portals, and, to a lesser extent, hooks. If you're in need of solutions with regards to iframes and React versions < 16, hit me up, I'll gladly offer advice.

Ad
source: stackoverflow.com
Ad