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 in which contains a function which has to be called when the iframe finishes loading. In that function i am setting the content but it doesnt seem like the onload function is called at all. I am testing it in 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?
TLDR; https://codesandbox.io/s/react-iframe-examples-36k1x
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 extisting 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 = () => (
<Frame>
<h1>Hello Content!</h1>
</Frame>
)
Further control, for example over an <iframe>
s <head>
contents, can easily be achieved as
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:
<iframe>
wants to programmatically control (as in decide about) its contents in a React-ish way.<iframe>
complies to the Same-origin policy.<iframe src="https://www.openpgp.org/>
kind of scenario.Use cases (that I know of);
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 on how to integrate some of the more popular styling strategies with iframes in React.
https://codesandbox.io/s/react-iframe-examples-36k1x
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.