ejemplos - javascript get inner html
Ejecutar elementos<script> insertados con.innerHTML (17)
Tengo un script que inserta algo de contenido en un elemento usando innerHTML
.
El contenido podría ser, por ejemplo:
<script type="text/javascript">alert(''test'');</script>
<strong>test</strong>
El problema es que el código dentro de la etiqueta <script>
no se ejecuta. Busqué en Google un poco, pero no hubo soluciones aparentes. Si $(element).append(content);
el contenido usando jQuery $(element).append(content);
las partes del script obtuvieron una eval
antes de ser inyectadas en el DOM.
¿Alguien ha recibido un fragmento de código que ejecuta todos los elementos del <script>
? El código de jQuery era un poco complejo, así que no pude entender cómo se hizo.
Editar :
Al echar un vistazo al código de jQuery he logrado averiguar cómo lo hace jQuery, lo que dio como resultado el siguiente código:
Demo:
<div id="element"></div>
<script type="text/javascript">
function insertAndExecute(id, text)
{
domelement = document.getElementById(id);
domelement.innerHTML = text;
var scripts = [];
ret = domelement.childNodes;
for ( var i = 0; ret[i]; i++ ) {
if ( scripts && nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
}
}
for(script in scripts)
{
evalScript(scripts[script]);
}
}
function nodeName( elem, name ) {
return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
}
function evalScript( elem ) {
data = ( elem.text || elem.textContent || elem.innerHTML || "" );
var head = document.getElementsByTagName("head")[0] || document.documentElement,
script = document.createElement("script");
script.type = "text/javascript";
script.appendChild( document.createTextNode( data ) );
head.insertBefore( script, head.firstChild );
head.removeChild( script );
if ( elem.parentNode ) {
elem.parentNode.removeChild( elem );
}
}
insertAndExecute("element", "<scri"+"pt type=''text/javascript''>document.write(''This text should appear as well.'')</scr"+"ipt><strong>this text should also be inserted.</strong>");
</script>
@phidah ... Aquí hay una solución muy interesante para su problema: http://24ways.org/2005/have-your-dom-and-script-it-too
Entonces se vería así:
<img src="empty.gif" onload="alert(''test'');this.parentNode.removeChild(this);" />
Aquí está mi solución en un proyecto reciente.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sample</title>
</head>
<body>
<h1 id="hello_world">Sample</h1>
<script type="text/javascript">
var div = document.createElement("div");
var t = document.createElement(''template'');
t.innerHTML = "Check Console tab for javascript output: Hello world!!!<br/><script type=''text/javascript'' >console.log(''Hello world!!!'');<//script>";
for (var i=0; i < t.content.childNodes.length; i++){
var node = document.importNode(t.content.childNodes[i], true);
div.appendChild(node);
}
document.body.appendChild(div);
</script>
</body>
</html>
Aquí hay un script más corto y eficiente que también funciona para scripts con la propiedad src
:
function insertAndExecute(id, text) {
document.getElementById(id).innerHTML = text;
var scripts = Array.prototype.slice.call(document.getElementById(id).getElementsByTagName("script"));
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src != "") {
var tag = document.createElement("script");
tag.src = scripts[i].src;
document.getElementsByTagName("head")[0].appendChild(tag);
}
else {
eval(scripts[i].innerHTML);
}
}
}
Nota: mientras que eval
puede causar una vulnerabilidad de seguridad si no se usa correctamente, es mucho más rápido que crear una etiqueta de script sobre la marcha.
Es más fácil usar jquery $(parent).html(code)
lugar de parent.innerHTML = code
:
var oldDocumentWrite = document.write;
var oldDocumentWriteln = document.writeln;
try {
document.write = function(code) {
$(parent).append(code);
}
document.writeln = function(code) {
document.write(code + "<br/>");
}
$(parent).html(html);
} finally {
$(window).load(function() {
document.write = oldDocumentWrite
document.writeln = oldDocumentWriteln
})
}
Esto también funciona con scripts que usan document.write
y scripts cargados a través del atributo src
. Lamentablemente, incluso esto no funciona con las secuencias de comandos de Google AdSense.
Extendiéndose fuera de Larry. Lo hice buscar recursivamente todo el bloque y los nodos de los niños.
La secuencia de comandos ahora también invocará las secuencias de comandos externas que se especifiquen con el parámetro src. Los guiones se anexan al encabezado en lugar de insertarse y colocarse en el orden en que se encuentran. Así que, específicamente, se preservan los guiones de órdenes. Y cada script se ejecuta de forma sincronizada, de forma similar a como el navegador maneja la carga DOM inicial. Entonces, si tiene un bloque de script que llama a jQuery desde un CDN y que el siguiente nodo de script usa jQuery ... ¡No hay problema! Oh y etiqueté los scripts anexados con una identificación serializada basada en lo que estableciste en el parámetro de etiqueta para que puedas encontrar lo que se agregó con este script.
exec_body_scripts: function(body_el, tag) {
// Finds and executes scripts in a newly added element''s body.
// Needed since innerHTML does not run scripts.
//
// Argument body_el is an element in the dom.
function nodeName(elem, name) {
return elem.nodeName && elem.nodeName.toUpperCase() ===
name.toUpperCase();
};
function evalScript(elem, id, callback) {
var data = (elem.text || elem.textContent || elem.innerHTML || "" ),
head = document.getElementsByTagName("head")[0] ||
document.documentElement;
var script = document.createElement("script");
script.type = "text/javascript";
if (id != '''') {
script.setAttribute(''id'', id);
}
if (elem.src != '''') {
script.src = elem.src;
head.appendChild(script);
// Then bind the event to the callback function.
// There are several events for cross browser compatibility.
script.onreadystatechange = callback;
script.onload = callback;
} else {
try {
// doesn''t work on ie...
script.appendChild(document.createTextNode(data));
} catch(e) {
// IE has funky script nodes
script.text = data;
}
head.appendChild(script);
callback();
}
};
function walk_children(node) {
var scripts = [],
script,
children_nodes = node.childNodes,
child,
i;
if (children_nodes === undefined) return;
for (i = 0; i<children_nodes.length; i++) {
child = children_nodes[i];
if (nodeName(child, "script" ) &&
(!child.type || child.type.toLowerCase() === "text/javascript")) {
scripts.push(child);
} else {
var new_scripts = walk_children(child);
for(j=0; j<new_scripts.length; j++) {
scripts.push(new_scripts[j]);
}
}
}
return scripts;
}
var i = 0;
function execute_script(i) {
script = scripts[i];
if (script.parentNode) {script.parentNode.removeChild(script);}
evalScript(scripts[i], tag+"_"+i, function() {
if (i < scripts.length-1) {
execute_script(++i);
}
});
}
// main section of function
if (tag === undefined) tag = ''tmp'';
var scripts = walk_children(body_el);
execute_script(i);
}
Gracias al guión de Larry, que funcionó perfectamente en IE10, esto es lo que he usado:
$(''#'' + id)[0].innerHTML = result;
$(''#'' + id + " script").each(function() { this.text = this.text || $(this).text();} );
La secuencia de comandos de OP no funciona en IE 7. Con la ayuda de SO, aquí hay una secuencia de comandos que hace:
exec_body_scripts: function(body_el) {
// Finds and executes scripts in a newly added element''s body.
// Needed since innerHTML does not run scripts.
//
// Argument body_el is an element in the dom.
function nodeName(elem, name) {
return elem.nodeName && elem.nodeName.toUpperCase() ===
name.toUpperCase();
};
function evalScript(elem) {
var data = (elem.text || elem.textContent || elem.innerHTML || "" ),
head = document.getElementsByTagName("head")[0] ||
document.documentElement,
script = document.createElement("script");
script.type = "text/javascript";
try {
// doesn''t work on ie...
script.appendChild(document.createTextNode(data));
} catch(e) {
// IE has funky script nodes
script.text = data;
}
head.insertBefore(script, head.firstChild);
head.removeChild(script);
};
// main section of function
var scripts = [],
script,
children_nodes = body_el.childNodes,
child,
i;
for (i = 0; children_nodes[i]; i++) {
child = children_nodes[i];
if (nodeName(child, "script" ) &&
(!child.type || child.type.toLowerCase() === "text/javascript")) {
scripts.push(child);
}
}
for (i = 0; scripts[i]; i++) {
script = scripts[i];
if (script.parentNode) {script.parentNode.removeChild(script);}
evalScript(scripts[i]);
}
};
No debe usar la propiedad innerHTML sino el método appendChild del nodo: un nodo en un árbol de documentos [HTML DOM]. De esta manera, más tarde puede llamar a su código inyectado.
Asegúrese de comprender que node.innerHTML
no es lo mismo que node.appendChild
. Es posible que desee pasar algún tiempo en la Referencia de cliente de Javascript para obtener más detalles y el DOM. Espero que lo siguiente ayude ...
La inyección de muestra funciona:
<html>
<head>
<title>test</title>
<script language="javascript" type="text/javascript">
function doOnLoad(){
addScript(''inject'',"function foo(){ alert(''injected''); }");
}
function addScript(inject,code){
var _in = document.getElementById(''inject'');
var scriptNode = document.createElement(''script'');
scriptNode.innerHTML = code;
_in.appendChild(scriptNode);
}
</script>
</head>
<body onload="doOnLoad();">
<div id="header">some content</div>
<div id="inject"></div>
<input type="button" onclick="foo(); return false;" value="Test Injected" />
</body>
</html>
Saludos,
Prueba este fragmento:
function stripAndExecuteScript(text) {
var scripts = '''';
var cleaned = text.replace(/<script[^>]*>([/s/S]*?)<//script>/gi, function(){
scripts += arguments[1] + ''/n'';
return '''';
});
if (window.execScript){
window.execScript(scripts);
} else {
var head = document.getElementsByTagName(''head'')[0];
var scriptElement = document.createElement(''script'');
scriptElement.setAttribute(''type'', ''text/javascript'');
scriptElement.innerText = scripts;
head.appendChild(scriptElement);
head.removeChild(scriptElement);
}
return cleaned;
};
var scriptString = ''<scrip'' + ''t + type="text/javascript">alert(/'test/');</scr'' + ''ipt><strong>test</strong>'';
document.getElementById(''element'').innerHTML = stripAndExecuteScript(scriptString);
Prueba esto, funciona para mí en Chrome, Safari y Firefox:
var script = document.createElement(''script'');
script.innerHTML = ''console.log("hi")'';
document.body.appendChild(script);
--> logs "hi"
Sin embargo, una cosa a tener en cuenta es que NO se ejecutará el siguiente script div-anidado:
var script = document.createElement(''div'');
script.innerHTML = ''<script>console.log("hi")</script>'';
document.body.appendChild(script);
--> doesn''t log anything
Para que se ejecute un script, debe crearse como un nodo y luego anexarse como un elemento secundario. Incluso puede agregar un script dentro de un div previamente inyectado y se ejecutará (me he encontrado con esto antes cuando intento que el código del servidor de anuncios funcione):
var div = document.createElement(''div'');
div.id = ''test-id'';
document.body.appendChild(div);
var script = document.createElement(''script'');
script.innerHTML = ''console.log("hi")'';
document.getElementById(''test-id'').appendChild(script);
--> logs "hi"
Prueba la función eval ().
data.newScript = ''<script type="text/javascript">//my script...</script>''
var element = document.getElementById(''elementToRefresh'');
element.innerHTML = data.newScript;
eval(element.firstChild.innerHTML);
Este es un ejemplo real de un proyecto que estoy desarrollando. Gracias a esta post
Puede echar un vistazo a esta publicación . El código podría verse así:
var actualDivToBeUpdated = document.getElementById(''test'');
var div = document.createElement(''div'');
div.innerHTML = ''<script type="text/javascript">alert("test");<//script>'';
var children = div.childNodes;
actualDivToBeUpdated.innerHTML = '''';
for(var i = 0; i < children.length; i++) {
actualDivToBeUpdated.appendChild(children[i]);
}
Solo haz:
document.body.innerHTML = ''<img src="../images/loaded.gif" alt="" onload="alert(/'test/');this.parentNode.removeChild(this);" />'';
Una solución sin usar "eval":
var setInnerHtml = function(elm, html) {
elm.innerHTML = html;
var scripts = elm.getElementsByTagName("script");
// If we don''t clone the results then "scripts"
// will actually update live as we insert the new
// tags, and we''ll get caught in an endless loop
var scriptsClone = [];
for (var i = 0; i < scripts.length; i++) {
scriptsClone.push(scripts[i]);
}
for (var i = 0; i < scriptsClone.length; i++) {
var currentScript = scriptsClone[i];
var s = document.createElement("script");
// Copy all the attributes from the original script
for (var j = 0; j < currentScript.attributes.length; j++) {
var a = currentScript.attributes[j];
s.setAttribute(a.name, a.value);
}
s.appendChild(document.createTextNode(currentScript.innerHTML));
currentScript.parentNode.replaceChild(s, currentScript);
}
}
Esto esencialmente clona la etiqueta de secuencia de comandos y luego reemplaza la etiqueta de secuencia de comandos bloqueada con la nueva etiqueta generada, lo que permite la ejecución.
Versión ES6 simplificada de la respuesta de @ joshcomley con un ejemplo
http://plnkr.co/edit/MMegiu?p=preview
var setInnerHtml = function(elm, html) {
elm.innerHTML = html;
Array.from(elm.querySelectorAll("script")).forEach(function(el) {
let newEl = document.createElement("script");
Array.from(el.attributes).forEach(function(el) {
newEl.setAttribute(el.name, el.value)
});
newEl.appendChild(document.createTextNode(el.innerHTML));
el.parentNode.replaceChild(newEl, el);
})
}
Uso
el.innerHTML = HTML; // does not run <script> in HTML
setInnerHTML(el, HTML); // execute <script> in HTML
scriptNode.innerHTML = code
no funcionó para IE. Lo único que hay que hacer es reemplazar con scriptNode.text = code
y funciona bien
function insertHtml(id, html)
{
var ele = document.getElementById(id);
ele.innerHTML = html;
var codes = ele.getElementsByTagName("script");
for(var i=0;i<codes.length;i++)
{
eval(codes[i].text);
}
}
Funciona en Chrome en mi proyecto