javascript html reactjs script-tag

javascript - Reaccionar: la etiqueta del script no funciona cuando se inserta usando dangerouslySetInnerHTML



reactjs script-tag (3)

Estoy tratando de configurar html enviado desde mi servidor para que se muestre dentro de un div usando la propiedad dangerouslySetInnerHTML en React. También tengo una etiqueta de script dentro y uso funciones definidas en el mismo dentro de ese html. He hecho un ejemplo de error en JSFiddle here .

Este es el código de prueba:

var x = ''<html><scr''+''ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr''+''ipt><body><p onClick="pClicked()">Hello</p></body></html>''; var Hello = React.createClass({ displayName: ''Hello'', render: function() { return (<div dangerouslySetInnerHTML={{__html: x}} />); } });

Verifiqué y la etiqueta de script se agrega a DOM, pero no puedo llamar a las funciones definidas dentro de esa etiqueta de script. Si esta no es la forma correcta, ¿hay alguna otra forma de inyectar el contenido de la etiqueta del script?


Aquí hay una manera un poco sucia de hacerlo, una pequeña explicación de lo que está sucediendo aquí, extrae el contenido del script a través de una expresión regular, y solo renderiza html usando react, luego, después de que el componente se monta el contenido en la etiqueta del script se ejecuta en un ámbito global.

var x = ''<html><scr''+''ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr''+''ipt><body><p onClick="pClicked()">Hello</p></body></html>''; var extractscript=/<script>(.+)<//script>/gi.exec(x); x=x.replace(extractscript[0],""); var Hello = React.createClass({ displayName: ''Hello'', componentDidMount: function() { // this runs the contents in script tag on a window/global scope window.eval(extractscript[1]); }, render: function() { return (<div dangerouslySetInnerHTML={{__html: x}} />); } }); ReactDOM.render( React.createElement(Hello), document.getElementById(''container'') );


No creo que necesites usar concatenación ( + ) aquí.

var x = ''<html><scr''+''ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr''+''ipt><body><p onClick="pClicked()">Hello</p></body></html>'';

Creo que solo puedes hacer:

var x = ''<html><script>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</script><body><p onClick="pClicked()">Hello</p></body></html>'';

Dado que de todos modos se pasa a dangerouslySetInnerHTML .

Pero volvamos al tema. No necesita usar expresiones regulares para acceder al contenido de la etiqueta del script. Si agrega el atributo id , por ejemplo <script id="myId">...</script> , puede acceder fácilmente al elemento.

Veamos un ejemplo de tal implementación.

const x = ` <html> <script id="myScript"> alert("this.is.sparta"); function pClicked() {console.log("p is clicked");} </script> <body> <p onClick="pClicked()">Hello</p> </body> </html> `; const Hello = React.createClass({ displayName: ''Hello'', componentDidMount() { const script = document.getElementById(''myScript'').innerHTML; window.eval(script); } render() { return <div dangerouslySetInnerHTML={{__html: x}} />; } });

Si tiene varias secuencias de comandos, puede agregar un atributo de datos [data-my-script] por ejemplo, y luego acceder a él utilizando jQuery:

const x = ` <html> <script data-my-script=""> alert("this.is.sparta"); function pClicked() {console.log("p is clicked");} </script> <script data-my-script=""> alert("another script"); </script> <body> <p onClick="pClicked()">Hello</p> </body> </html> `; const Hello = React.createClass({ constructor(props) { super(props); this.helloElement = null; } displayName: ''Hello'', componentDidMount() { $(this.helloElement).find(''[data-my-script]'').each(function forEachScript() { const script = $(this).text(); window.eval(script); }); } render() { return ( <div ref={helloElement => (this.helloElement = helloElement)} dangerouslySetInnerHTML={{__html: x}} /> ); } });

En cualquier caso, siempre es bueno evitar usar eval , por lo que otra opción es obtener el texto y agregar una nueva etiqueta de script con el contenido del script original en lugar de llamar a eval . Esta respuesta sugiere tal enfoque


una pequeña extensión para la respuesta de Dasith para futuras vistas ...

Tuve un problema muy similar, pero en mi caso obtuve el HTML del lado del servidor y me tomó un tiempo (parte de la solución de informes donde el back-end representará el informe en html)

así que lo que hice fue muy similar solo que manejé el script que se ejecuta en la función componentWillMount ():

import React from ''react''; import jsreport from ''jsreport-browser-client-dist'' import logo from ''./logo.svg''; import ''./App.css''; class App extends React.Component { constructor() { super() this.state = { report: "", reportScript: "" } } componentWillMount() { jsreport.serverUrl = ''http://localhost:5488''; let reportRequest = {template: {shortid: ''HJH11D83ce''}} // let temp = "this is temp" jsreport.renderAsync(reportRequest) .then(res => { let htmlResponse = res.toString() let extractedScript = /<script>[/s/S]*<//script>/g.exec(htmlResponse)[0]; // console.log(''html is: '',htmlResponse) // console.log(''script is: '',extractedScript) this.setState({report: htmlResponse}) this.setState({reportScript: extractedScript}) }) } render() { let report = this.state.report return ( <div className="App"> <div className="App-header"> <img src={logo} className="App-logo" alt="logo"/> <h2>Welcome to React</h2> </div> <div id="reportPlaceholder"> <div dangerouslySetInnerHTML={{__html: report}}/> </div> </div> ); } componentDidUpdate() { // this runs the contents in script tag on a window/global scope let scriptToRun = this.state.reportScript if (scriptToRun !== undefined) { //remove <script> and </script> tags since eval expects only code without html tags let scriptLines = scriptToRun.split("/n") scriptLines.pop() scriptLines.shift() let cleanScript = scriptLines.join("/n") console.log(''running script '',cleanScript) window.eval(cleanScript) } } } export default App;

Espero que esto sea útil ...