use*_*940 20 javascript iframe xmlhttprequest reactjs
我试图在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;
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
Luk*_*ger 35
编辑10-25-2018
在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>
)
}
}
Run Code Online (Sandbox Code Playgroud)
使用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>
)
}
Run Code Online (Sandbox Code Playgroud)
用法:
import Frame from './Frame'
const MyComp = () => <Frame><h1>Hello Content!</h1></Frame>
Run Code Online (Sandbox Code Playgroud)
进一步的控制,例如在不同iframe <head>,可以容易地实现作为本主旨所示.
还有react-frame-component,一个包,恕我直言提供了在React中使用iframe时所需的一切.
SSR
有一件事(至少据我所知),你几乎无法用Portal实现,是服务器端渲染,因为当它们具有对实际DOM节点的引用时,它们只能被渲染.所以,如果你......
srcdociframe上的属性...然后你可以从这样的事情开始:
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
}
/>
)
}
}
Run Code Online (Sandbox Code Playgroud)
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();
}
});
Run Code Online (Sandbox Code Playgroud)
现在,这不是非常适合组成的.你不能使用React.props.children.only和喜欢,因为这些总是指向已经编译/创建的元素,这些元素已经是diff树的一部分.而且因为我们想要一个新的框架内容的差异树,你必须为每个框架内容定义一个新的组件.
输入高阶订单组件.目标是创建一种装饰器,可以应用于您想要框架的任何元素:
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();
}
});
}
Run Code Online (Sandbox Code Playgroud)
使用这样:
var MyFramedComponent = FramedComponent(MyComponent);
Run Code Online (Sandbox Code Playgroud)
编辑3-7-2017 React的主要优点之一可能是其所有其他当前流行的虚拟DOM库,其合成事件系统.良好的法律,它只是工作和泡沫非常方便.
但是,通过这种方法,您(非常有意)地从下一个中删除一个差异树,对于事件系统也是如此.对于大多数没有太大影响的事件:
var logNestedClicks = function(event) {
console.log(event);
}
// and then
<MyFramedComponent onClick={logNestedClicks) />
Run Code Online (Sandbox Code Playgroud)
这样做会很好.但是有一些不那么突出的例外,特别是考虑到React中的受控iFrame通常不仅仅用于创建范围这一事实,只是没有按预期工作.一个又一个不那么突出的例子:onBeforeInsert.这使得范围草案实例成为一项非常繁琐的任务.然后,这可能与大多数用例无关.在React中为iFrames制作(WYSIWYG)案例之前,请确保以您期望的方式捕获您的狗屎.去过那里,做到了,相信我.
如果有人只想在iframe中显示小的HTML,则有一个更简单的解决方案。
<iframe src={"data:text/html,"+encodeURIComponent(content)}/>
Run Code Online (Sandbox Code Playgroud)
内容的最大长度为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} />
Run Code Online (Sandbox Code Playgroud)
更多信息:https : //developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe