我正在尝试在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;
我做错了什么?
答案 0 :(得分:27)
修改10-25-2018
随着Portals in React 16的引入,整个框架的内容变得微不足道。不仅实现和使用更直接,iframe内容也是«parent»虚拟dom的实际子节点,这意味着共享事件系统,上下文等。棒极了吧?
import React, { Component } from 'react'
import { createPortal } from 'react-dom'
export default class Frame extends Component {
constructor(props) {
super(props)
this.setContentRef = node =>
(this.contentRef =
((!node || !node.contentWindow) && null) ||
node.contentWindow.document.body)
}
render() {
const { children, ...props } = this.props // eslint-disable-line
return (
<iframe {...props} ref={this.setContentRef}>
{this.contentRef &&
createPortal(
React.Children.only(children),
this.contentRef
)}
</iframe>
)
}
}
使用React Hooks时,它变得更加简洁:
import React, { useState } from 'react'
import { createPortal } from 'react-dom'
export const IFrame = ({ children, ...props }) => {
const [contentRef, setContentRef] = useState(null)
const mountNode = contentRef && contentRef.contentWindow.document.body
return (
<iframe {...props} ref={setContentRef}>
{mountNode &&
createPortal(
React.Children.only(children),
mountNode
)}
</iframe>
)
}
用法:
import Frame from './Frame'
const MyComp = () => <Frame><h1>Hello Content!</h1></Frame>
this Gist显示,可以轻松实现对iframe <head>
的进一步控制。
还有react-frame-component,一个包,恕我直言提供了在React中使用iframe时所需的一切。
<强> SSR 强>
有一件事(至少据我所知),你几乎无法用Portal实现,是服务器端渲染,因为当它们具有对实际DOM节点的引用时,它们只能被渲染。 所以,如果你...
srcdoc
attribute on iframes ...然后你可以从这样的事情开始:
import React, { Component } from 'react'
import { renderToString } from 'react-dom/server'
import { hydrate, render } from 'react-dom'
const wrapWithMountNode = html => {
return `<!DOCTYPE html><html><head></head><body><div id="frame">${html}</div></body></html>`.trim()
}
export default class SSRFrame extends Component {
constructor(props) {
super(props)
this.initialMarkup = wrapWithMountNode(
renderToString(
React.Children.only(this.props.children)
)
)
this.contentRef = null
this.setContentRef = node => {
this.contentRef =
((!node || !node.contentWindow) && null) ||
node.contentWindow.document.getElementById('frame')
}
}
componentDidMount() {
this.contentRef &&
hydrate(
React.Children.only(this.props.children),
this.contentRef
)
}
componentDidUpdate() {
this.contentRef &&
render(
React.Children.only(this.props.children),
this.contentRef
)
}
componentWillUnmount() {
this.contentRef = null
}
render() {
const { children, ...props } = this.props // eslint-disable-line
return (
<iframe
{...props}
ref={this.setContentRef}
srcDoc={
(!this.contentRef && this.initialMarkup) ||
undefined
}
/>
)
}
}
2018年的快乐框架!
原帖
就IFrame而言,解决方案实际上非常简单:您必须为IFrame内容创建一个新的DOM渲染器,并将其与应用程序的其余部分同步。这样的组件看起来像这样:
var React = require('react');
var ReactDOM = require('react-dom');
var IFrame = React.createClass({
propTypes = {
frameProps: React.PropTypes.object,
},
defaultProps = {
frameProps: {
frameBorder: 0
}
},
updateIFrameContents: function() {
ReactDOM.render((
// Here you put the components that should be in the IFrame
<div {...this.props}>Hello IFrame!</div>
), this.el);
},
render: function() {
return (
<iframe {...this.props.frameProps} />
);
},
componentDidMount: function() {
var frameBody = ReactDOM.findDOMNode(this).contentDocument.body,
el = document.createElement('div');
frameBody.appendChild(el);
this.el = el;
this.updateIFrameContents();
},
componentDidUpdate: function() {
this.updateIFrameContents();
}
});
现在,这不是非常适合作曲的。你不能使用React.props.children.only
之类的东西,因为它们总是指向已经编译/创建的元素,这些元素已经是diff树的一部分。而且因为我们想要一个新的框架内容的差异树,你必须为每个框架内容定义一个新的组件。
输入Higher Order Components。目标是创建一种装饰器,可以应用于您想要框架的任何元素:
function FramedComponent(Component) {
return React.createClass({
propTypes = {
frameProps: React.PropTypes.object,
},
defaultProps = {
frameProps: {
frameBorder: 0
}
},
updateIFrameContents: function() {
ReactDOM.render((
<Component {...this.props} />
), this.el);
},
render: function() {
return (
<iframe {...this.props.frameProps} />
);
},
componentDidMount: function() {
var frameBody = ReactDOM.findDOMNode(this).contentDocument.body,
el = document.createElement('div');
frameBody.appendChild(el);
this.el = el;
this.updateIFrameContents();
},
componentDidUpdate: function() {
this.updateIFrameContents();
}
});
}
像这样使用:
var MyFramedComponent = FramedComponent(MyComponent);
编辑3-7-2017 React可能是所有其他当前流行的虚拟DOM库的主要优点之一是它的合成事件系统。良好的法律,它只是工作和泡沫非常方便。
通过这种方法,你(非常有意)从下一个中删除一个diff树,对于事件系统也是如此。对于大多数没有太大影响的事件:
var logNestedClicks = function(event) {
console.log(event);
}
// and then
<MyFramedComponent onClick={logNestedClicks) />
这样可以正常工作。但是有一些不那么突出的例外,特别是考虑到React中的受控iFrame通常不仅仅用于创建范围这一事实,只是没有按预期工作。一个又一个不那么突出的例子:onBeforeInsert
。这使得范围Draft实例成为一项非常繁琐的任务。然后,这可能与大多数用例无关。在React中为iFrames制作(WYSIWYG)案例之前,请确保以您期望的方式捕获您的狗屎。去过那里,完成了,believe me。
答案 1 :(得分:2)
如果有人只想在iframe中显示小的HTML,则有一个更简单的解决方案。
<iframe src={"data:text/html,"+encodeURIComponent(content)}/>
内容的最大长度为32768个字符。
在接受的答案中也提到了易于使用的react-frame-component软件包。
答案 2 :(得分:1)
您可以使用iframe的srcdoc
属性。它将起作用!
srcdoc:要嵌入的内联HTML,覆盖src属性。
阅读:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
答案 3 :(得分:0)
这也有效(IE不支持)。
const myHTML = <h1>Hello World</h1>
<iframe srcDoc={myHTML} />
此处有更多信息:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
答案 4 :(得分:0)
使用DOMParser constructor's parseFromString来解析html比接受的答案要简单一些。这是一个示例,其中从DOMParser的生成文档中检索已解析的html。如果您要向iframe发送元素,请忽略.body.innerText
的{{1}}部分。
parseHtml