addeventlistener not working - javascript

(function() {
var divs = document.getElementsByClassName('data');
var myFunction = function()
{
alert("hello");
var el = this;
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"Either no transform set, or browser doesn't do getComputedStyle";
console.log(tr);
}
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('mouseover', myFunction, true);
}
})();
<div class="data">data1</div>
<div class="data">data2</div>
<div class="data">data3</div>
<div class="data">data4</div>
I am using pure javascript to add event listener to the div .
The add event listener not working
Some guys said to me to add the event listener at window.load but i am getting my dom objects here in this javascript.
Any help?

There is no error in your code. you just need to put your code below the DOM.
Best place to add your script is before the end of body tag. Use external script files instead of internal of script.
For example only I am using script inside html. You should use script as external file which is best practice.
Working Code -
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div class="data">data1</div>
<div class="data">data2</div>
<div class="data">data3</div>
<div class="data">data4</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
(function() {
var divs = document.getElementsByClassName('data');
var myFunction = function()
{
alert("hello");
var el = this;
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"Either no transform set, or browser doesn't do getComputedStyle";
console.log(tr);
};
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('mouseover', myFunction, true);
}
})();
});
</script>
</body>
</html>
DOMContentLoaded event will execute your code after DOM is loaded and parsed. so it will not give error.

Your code works. You must run your script after DOM is loaded, or you must write
DOMContentLoaded eventListener:
document.addEventListener("DOMContentLoaded", function(event) {
(function() {
var divs = document.getElementsByClassName('data');
var myFunction = function()
{
//.... Your code
}
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('mouseover', myFunction, true);
}
})();
});

Related

load script by changing innerHTML [duplicate]

I've got a script that inserts some content into an element using innerHTML.
The content could for example be:
<script type="text/javascript">alert('test');</script>
<strong>test</strong>
Problem is that the code inside the <script> tag doesn't get executed.
I googled it a bit but there were no apparent solutions. If I inserted the content using jQuery $(element).append(content);the script parts got eval'd before being injected into the DOM.
Has anyone got a snippet of code that executes all the <script> elements? The jQuery code was a bit complex so I couldn't really figure out how it was done.
Edit:
By peeking into the jQuery code I've managed to figure out how jQuery does it, which resulted in the following code:
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>
Simplified ES6 version of #joshcomley's answer with an example.
No JQuery, No library, No eval, No DOM change, Just pure Javascript.
http://plnkr.co/edit/MMegiu?p=preview
function setInnerHTML(elm, html) {
elm.innerHTML = html;
Array.from(elm.querySelectorAll("script"))
.forEach( oldScriptEl => {
const newScriptEl = document.createElement("script");
Array.from(oldScriptEl.attributes).forEach( attr => {
newScriptEl.setAttribute(attr.name, attr.value)
});
const scriptText = document.createTextNode(oldScriptEl.innerHTML);
newScriptEl.appendChild(scriptText);
oldScriptEl.parentNode.replaceChild(newScriptEl, oldScriptEl);
});
}
Usage
$0.innerHTML = HTML; // does *NOT* run <script> tags in HTML
setInnerHTML($0, HTML); // does run <script> tags in HTML
Here is a very interesting solution to your problem:
http://24ways.org/2005/have-your-dom-and-script-it-too
So it would look like this instead:
<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />
You should not use the innerHTML property but rather the appendChild method of the Node: a node in a document tree [HTML DOM]. This way you are able to later call your injected code.
Make sure that you understand that node.innerHTML is not the same as node.appendChild. You might want to spend some time on the Javascript Client Reference for more details and the DOM. Hope the following helps...
Sample injection works:
<!DOCTYPE HTML>
<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>
The OP's script doesn't work in IE 7. With help from SO, here's a script that does:
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]);
}
};
Here's a shorter, more efficient script that also works for scripts with the src property:
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);
}
}
}
Note: whilst eval may cause a security vulnerability if not used properly, it is much faster than creating a script tag on the fly.
Try this snippet:
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);
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);
}
}
It works in Chrome in my project
A solution without using "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);
}
}
This essentially clones the script tag and then replaces the blocked script tag with the newly generated one, thus allowing execution.
scriptNode.innerHTML = code didn't work for IE. The only thing to do is replace with scriptNode.text = code and it work fine
It's easier to use jquery $(parent).html(code) instead of 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
})
}
This also works with scripts that use document.write and scripts loaded via src attribute. Unfortunately even this doesn't work with Google AdSense scripts.
Try this, it works for me on Chrome, Safari & Firefox:
var script = document.createElement('script');
script.innerHTML = 'console.log("hi")';
document.body.appendChild(script);
--> logs "hi"
One thing to note though, is that the following div-nested script will NOT run:
var script = document.createElement('div');
script.innerHTML = '<script>console.log("hi")</script>';
document.body.appendChild(script);
--> doesn't log anything
For a script to run it has to be created as a node then appended as a child. You can even append a script inside a previously injected div & it will run (I've run into this before when trying to get ad server code to work):
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"
Execute script element by normal .innerHTML just don't put "" start and closing tags in .innerhtml value. Have a look on my code, it's just simple as normal code no jQuery or any other long JavaScript function
<h2>Click on Run to execute script</h2>
<button type="button" onclick="run()">Run</button>
<script id="my_script"></script>
<script>
document.getElementById("my_script").innerHTML= "function run(){alert('Wow! Script executed :)');}";
</script>
Extending off of Larry's. I made it recursively search the entire block and children nodes.
The script now will also call external scripts that are specified with src parameter.
Scripts are appended to the head instead of inserted and placed in the order they are found. So specifically order scripts are preserved. And each script is executed synchronously similar to how the browser handles the initial DOM loading. So if you have a script block that calls jQuery from a CDN and than the next script node uses jQuery... No prob! Oh and I tagged the appended scripts with a serialized id based off of what you set in the tag parameter so you can find what was added by this 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);
}
Just do:
document.body.innerHTML = document.body.innerHTML + '<img src="../images/loaded.gif" alt="" onload="alert(\'test\');this.parentNode.removeChild(this);" />';
Made this new helper function in TypeScript, maybe someone will appreciate it. If you remove type declaration from script parameter it will just be plain JS.
const evalPageScripts = () => {
const scripts = document.querySelectorAll('script');
scripts.forEach((script: HTMLScriptElement) => {
const newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = script.src;
if (script.parentNode) {
script.parentNode.removeChild(script);
}
return document.body.appendChild(newScript);
})
};
export default evalPageScripts;
Using the short and sweet approach from https://stackoverflow.com/a/62641523/3394495 :
It first parses the HTML to a DocumentFragement (using createContextualFragment which is supported in all modern browsers, yet marked experimental) and then adds that to the DOM.
This will execute inline scripts.
export function setInnerHTMLAndExecuteScripts(element: HTMLElement, html: string) {
const newContent = document.createRange().createContextualFragment(html);
element.innerHTML = '';
element.append(newContent);
}
You may take a look at this post. The code might look like this:
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]);
}
Thanks to Larry's script, which worked perfectly well in IE10, this is what I've used:
$('#' + id)[0].innerHTML = result;
$('#' + id + " script").each(function() { this.text = this.text || $(this).text();} );
Here is my solution in a recent project.
<!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>
Expending the answer of Lambder
document.body.innerHTML = '<img src="../images/loaded.gif" alt="" > onload="alert(\'test\');this.parentNode.removeChild(this);" />';
You can use base64 image to create and load your script
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAADUlEQVQYV2P4//8/AwAI/AL+iF8G4AAAAABJRU5ErkJggg=="
onload="var script = document.createElement('script'); script.src = './yourCustomScript.js'; parentElement.append(script);" />
Or if you have a Iframe you can use it instead
<iframe src='//your-orginal-page.com' style='width:100%;height:100%'
onload="var script = document.createElement('script'); script.src = './your-coustom-script.js'; parentElement.append(script);"
frameborder='0'></iframe>
I needed something similar, but needed the script to remain or be re-created in the same spot as the original script, since my script targets the location of the script tag in the DOM to create/target elements. I also made the script recursive to make sure it also works if it is more than one level down.
NOTE: I use const here, if you have a older browser, just use var.
window.exec_body_scripts = function(body_el) {
// ref: https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml based on Larry K's answer
// 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.
const
type__Js = 'text/javascript',
tagName__Script = 'script',
tagName__Script__Upper = tagName__Script.toUpperCase();
var scripts = [], script, i;
function evalScript(elem) {
var parent = elem.parentNode,
data = (elem.text || elem.textContent || elem.innerHTML || ""),
script = document.createElement(tagName__Script);
script.type = type__Js;
try {
// doesn't work on ie...
script.appendChild(document.createTextNode(data));
} catch (e) {
// IE has funky script nodes
script.text = data;
}
// Make sure to re-insert the script at the same position
// to make sure scripts that target their position
// in the DOM function as expected.
var parent = elem.parentNode;
parent.insertBefore(script, elem);
parent.removeChild(elem);
};
// Get all scripts (recursive)
if (typeof (document.querySelectorAll) !== typeof (void 0)) {
document.querySelectorAll('script').forEach((scr) => { if (!scr.type || scr.type.toLowerCase() === type__Js) scripts.push(scr); });
}
else {
var children_nodes = body_el.childNodes, child;
for (i = 0; children_nodes[i]; i++) {
child = children_nodes[i];
if (
child.nodeName
&&
child.nodeName.toUpperCase() === tagName__Script__Upper
&&
(
!child.type
||
child.type.toLowerCase() === type__Js
)
) {
scripts.push(child);
}
// Recursive call
window.exec_body_scripts(child);
}
}
for (i = 0; scripts[i]; i++) {
evalScript(scripts[i]);
}
};
I had also pages that needed to execute javascript code (with eval) after the javascript files are loaded so did put load events on the script createelement so when the javascript file(s) were loaded gets executed. My website is a MVC SPA application. It loads partial pages with ajax. Those partial pages are set with innerhtml and then the javascript files are loaded and the javascript code on thst partial page.
Try function eval().
data.newScript = '<script type="text/javascript">//my script...</script>'
var element = document.getElementById('elementToRefresh');
element.innerHTML = data.newScript;
eval(element.firstChild.innerHTML);
This is a real example from a project that i am developing.
Thanks to this post

Non-independence of two javascript scripts

He Guys,
I have two scripts that work fine separately. One is for loading images and one is for loading Youtube iframe embeds.
However they don't seem to work together. Could you help out?
<iframe width="560" height="315" frameborder="0" data-src="https://www.youtube.com/embed/fKnbOJ4NAvS" src=""></iframe>
<a rel="nofollow" target="_blank" href="https://plus.google.com/share?url=https%3A%2F%2Fwww.domain.com"><img src="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-src="googleplus.png"></a>
<script>
function init() {
var imgDefer = document.getElementsByTagName('img');
for (var i=0; i<imgDefer.length; i++) {
if(imgDefer[i].getAttribute('data-src')) {
imgDefer[i].setAttribute('src',imgDefer[i].getAttribute('data-src'));
} } }
window.onload = init;
</script>
<script>
function init() {
var vidDefer = document.getElementsByTagName('iframe');
for (var i=0; i<vidDefer.length; i++) {
if(vidDefer[i].getAttribute('data-src')) {
vidDefer[i].setAttribute('src',vidDefer[i].getAttribute('data-src'));
} } }
window.onload = init;
</script>
You have made a couple of invalid assumptions.
Firstly, all scripts occupy the same global name space. Multiple <script>...</script> tags are not independent, therefore.
<script>
//script 1
</script>
<script>
//script 2
</script>
is equivalent to :
<script>
//script 1
//script 2
</script>
Secondly, repeated assignments of functions to window.onload are not cumulative. With window.onload = init followed by a second window.onload = init, the second assignment will override the first.
Now you should understand that your second script nullifies the first.
To fix, you could give the two functions unique names, and call them from a single (anonymous) window.onload handler :
<script>
function init_1() {
var imgElements = document.getElementsByTagName('img');
for (var i=0; i<imgElements.length; i++) {
if(imgElements[i].getAttribute('data-src')) {
imgElements[i].setAttribute('src', imgElements[i].getAttribute('data-src'));
}
}
}
function init_2() {
var vidElements = document.getElementsByTagName('iframe');
for (var i=0; i<vidElements.length; i++) {
if(vidElements[i].getAttribute('data-src')) {
vidElements[i].setAttribute('src', vidElements[i].getAttribute('data-src'));
}
}
}
window.onload = function() {
init_1();
init_2();
};
</script>
You could alternatively omit init_1() and init_2(), and write everything direcly inside an anonymous window.onload handler :
<script>
window.onload = function() {
var imgElements = document.getElementsByTagName('img');
var vidElements = document.getElementsByTagName('iframe');
var i;
for (i=0; i<imgElements.length; i++) {
if(imgElements[i].getAttribute('data-src')) {
imgElements[i].setAttribute('src', imgElements[i].getAttribute('data-src'));
}
}
for (i=0; i<vidElements.length; i++) {
if(vidElements[i].getAttribute('data-src')) {
vidElements[i].setAttribute('src', vidElements[i].getAttribute('data-src'));
}
}
};
</script>
It is perfectly OK to reuse the variable i in this way.
You will notice that I renamed you variables to avoid "Defer", which has a very specific meaning in JavaScript.

How to collect all script tags of HTML page in a variable

I would like to collect all the <script> ....</script> code section present in the HTML page in some variable.
What should be the simpler way to do this, Any idea how it can be retrieved using JavaScript.??
Any help will be greatly appreciated.
To get a list of scripts you can use
document.getElementsByTagName("script"); by tag
document.scripts; Built-in collection
document.querySelectorAll("script"); by selector
$("script") jQuery by selector
var scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src) console.log(i, scripts[i].src)
else console.log(i, scripts[i].innerHTML)
}
// To get the content of the external script
// - I use jQuery here - only works if CORS is allowing it
// find the first script from google
var url = $("script[src*='googleapis']")[0].src;
$.get(url,function(data) { // get the source
console.log(data.split("|")[0]); // show version info
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
console.log("Inline script");
</script>
<script>
function bla() {
console.log("Other inline script");
}
</script>
The simplest way is probably document.scripts
You would do:
var scripts = document.getElementsByTagName( 'script' );
Now scripts is a NodeList (like an array), and you can access each one using scripts[0], scripts[1] and so on.
try this
var scripts = document.getElementsByTagName("script");
Without jQuery :
var scripts = document.getElementsByTagName("script");
With jQuery :
var scripts = $("script");
Here you go --
(function () {
'use strict';
let logscript = function () {
let js = document.scripts;
for (let i = 0; i < js.length; i++) {
if (js[i].src) {
console.log(i, js[i].src);
} else {
console.log(i, js[i].innerHTML);
}
}
};
if (document.readyState === 'complete') {
logscript();
} else {
window.addEventListener('load', logscript);
}
})();

Appending target="_blank" to links in an iframe without using <body onload>

I'm trying to change the links in an iframe to load in a new window instead of the iframe itself. Currently I use this code in head:
$(document).ready(function() {
var oIFrame = document.getElementById("iframeID");
var oDoc = (oIFrame.contentWindow || oIFrame.contentDocument);
if(oDoc.document) oDoc = oDoc.document;
var links = oDoc.getElementsByTagName("a");
for(var i=0; i<links.length; i++) { links[i].target="_blank"; }
});
However, the code above is triggered before the iframe is fully loaded with its contents. I know this code would work if it's triggered in the body onload attribute, but I'd like to avoid that method and implement it in a function or a file instead.
Try
$("#iframeid").load(function(){
// your code
});
Have a go with:
$(document).ready(function() {
var oIFrame = document.getElementById("iframeID");
var oDoc = (oIFrame.contentWindow || oIFrame.contentDocument);
if(oDoc.document) oDoc = oDoc.document;
$(oDoc).ready(function(){
var links = oDoc.getElementsByTagName("a");
for(var i=0; i<links.length; i++) { links[i].target="_blank"; }
}
});
You could set a timeout function periodically checking if
iframe.document.readyState == 'complete'

Executing <script> elements inserted with .innerHTML

I've got a script that inserts some content into an element using innerHTML.
The content could for example be:
<script type="text/javascript">alert('test');</script>
<strong>test</strong>
Problem is that the code inside the <script> tag doesn't get executed.
I googled it a bit but there were no apparent solutions. If I inserted the content using jQuery $(element).append(content);the script parts got eval'd before being injected into the DOM.
Has anyone got a snippet of code that executes all the <script> elements? The jQuery code was a bit complex so I couldn't really figure out how it was done.
Edit:
By peeking into the jQuery code I've managed to figure out how jQuery does it, which resulted in the following code:
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>
Simplified ES6 version of #joshcomley's answer with an example.
No JQuery, No library, No eval, No DOM change, Just pure Javascript.
http://plnkr.co/edit/MMegiu?p=preview
function setInnerHTML(elm, html) {
elm.innerHTML = html;
Array.from(elm.querySelectorAll("script"))
.forEach( oldScriptEl => {
const newScriptEl = document.createElement("script");
Array.from(oldScriptEl.attributes).forEach( attr => {
newScriptEl.setAttribute(attr.name, attr.value)
});
const scriptText = document.createTextNode(oldScriptEl.innerHTML);
newScriptEl.appendChild(scriptText);
oldScriptEl.parentNode.replaceChild(newScriptEl, oldScriptEl);
});
}
Usage
$0.innerHTML = HTML; // does *NOT* run <script> tags in HTML
setInnerHTML($0, HTML); // does run <script> tags in HTML
Here is a very interesting solution to your problem:
http://24ways.org/2005/have-your-dom-and-script-it-too
So it would look like this instead:
<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />
You should not use the innerHTML property but rather the appendChild method of the Node: a node in a document tree [HTML DOM]. This way you are able to later call your injected code.
Make sure that you understand that node.innerHTML is not the same as node.appendChild. You might want to spend some time on the Javascript Client Reference for more details and the DOM. Hope the following helps...
Sample injection works:
<!DOCTYPE HTML>
<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>
The OP's script doesn't work in IE 7. With help from SO, here's a script that does:
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]);
}
};
Here's a shorter, more efficient script that also works for scripts with the src property:
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);
}
}
}
Note: whilst eval may cause a security vulnerability if not used properly, it is much faster than creating a script tag on the fly.
Try this snippet:
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);
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);
}
}
It works in Chrome in my project
A solution without using "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);
}
}
This essentially clones the script tag and then replaces the blocked script tag with the newly generated one, thus allowing execution.
scriptNode.innerHTML = code didn't work for IE. The only thing to do is replace with scriptNode.text = code and it work fine
It's easier to use jquery $(parent).html(code) instead of 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
})
}
This also works with scripts that use document.write and scripts loaded via src attribute. Unfortunately even this doesn't work with Google AdSense scripts.
Try this, it works for me on Chrome, Safari & Firefox:
var script = document.createElement('script');
script.innerHTML = 'console.log("hi")';
document.body.appendChild(script);
--> logs "hi"
One thing to note though, is that the following div-nested script will NOT run:
var script = document.createElement('div');
script.innerHTML = '<script>console.log("hi")</script>';
document.body.appendChild(script);
--> doesn't log anything
For a script to run it has to be created as a node then appended as a child. You can even append a script inside a previously injected div & it will run (I've run into this before when trying to get ad server code to work):
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"
Execute script element by normal .innerHTML just don't put "" start and closing tags in .innerhtml value. Have a look on my code, it's just simple as normal code no jQuery or any other long JavaScript function
<h2>Click on Run to execute script</h2>
<button type="button" onclick="run()">Run</button>
<script id="my_script"></script>
<script>
document.getElementById("my_script").innerHTML= "function run(){alert('Wow! Script executed :)');}";
</script>
Extending off of Larry's. I made it recursively search the entire block and children nodes.
The script now will also call external scripts that are specified with src parameter.
Scripts are appended to the head instead of inserted and placed in the order they are found. So specifically order scripts are preserved. And each script is executed synchronously similar to how the browser handles the initial DOM loading. So if you have a script block that calls jQuery from a CDN and than the next script node uses jQuery... No prob! Oh and I tagged the appended scripts with a serialized id based off of what you set in the tag parameter so you can find what was added by this 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);
}
Just do:
document.body.innerHTML = document.body.innerHTML + '<img src="../images/loaded.gif" alt="" onload="alert(\'test\');this.parentNode.removeChild(this);" />';
Made this new helper function in TypeScript, maybe someone will appreciate it. If you remove type declaration from script parameter it will just be plain JS.
const evalPageScripts = () => {
const scripts = document.querySelectorAll('script');
scripts.forEach((script: HTMLScriptElement) => {
const newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = script.src;
if (script.parentNode) {
script.parentNode.removeChild(script);
}
return document.body.appendChild(newScript);
})
};
export default evalPageScripts;
Using the short and sweet approach from https://stackoverflow.com/a/62641523/3394495 :
It first parses the HTML to a DocumentFragement (using createContextualFragment which is supported in all modern browsers, yet marked experimental) and then adds that to the DOM.
This will execute inline scripts.
export function setInnerHTMLAndExecuteScripts(element: HTMLElement, html: string) {
const newContent = document.createRange().createContextualFragment(html);
element.innerHTML = '';
element.append(newContent);
}
You may take a look at this post. The code might look like this:
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]);
}
Thanks to Larry's script, which worked perfectly well in IE10, this is what I've used:
$('#' + id)[0].innerHTML = result;
$('#' + id + " script").each(function() { this.text = this.text || $(this).text();} );
Here is my solution in a recent project.
<!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>
Expending the answer of Lambder
document.body.innerHTML = '<img src="../images/loaded.gif" alt="" > onload="alert(\'test\');this.parentNode.removeChild(this);" />';
You can use base64 image to create and load your script
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAADUlEQVQYV2P4//8/AwAI/AL+iF8G4AAAAABJRU5ErkJggg=="
onload="var script = document.createElement('script'); script.src = './yourCustomScript.js'; parentElement.append(script);" />
Or if you have a Iframe you can use it instead
<iframe src='//your-orginal-page.com' style='width:100%;height:100%'
onload="var script = document.createElement('script'); script.src = './your-coustom-script.js'; parentElement.append(script);"
frameborder='0'></iframe>
I needed something similar, but needed the script to remain or be re-created in the same spot as the original script, since my script targets the location of the script tag in the DOM to create/target elements. I also made the script recursive to make sure it also works if it is more than one level down.
NOTE: I use const here, if you have a older browser, just use var.
window.exec_body_scripts = function(body_el) {
// ref: https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml based on Larry K's answer
// 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.
const
type__Js = 'text/javascript',
tagName__Script = 'script',
tagName__Script__Upper = tagName__Script.toUpperCase();
var scripts = [], script, i;
function evalScript(elem) {
var parent = elem.parentNode,
data = (elem.text || elem.textContent || elem.innerHTML || ""),
script = document.createElement(tagName__Script);
script.type = type__Js;
try {
// doesn't work on ie...
script.appendChild(document.createTextNode(data));
} catch (e) {
// IE has funky script nodes
script.text = data;
}
// Make sure to re-insert the script at the same position
// to make sure scripts that target their position
// in the DOM function as expected.
var parent = elem.parentNode;
parent.insertBefore(script, elem);
parent.removeChild(elem);
};
// Get all scripts (recursive)
if (typeof (document.querySelectorAll) !== typeof (void 0)) {
document.querySelectorAll('script').forEach((scr) => { if (!scr.type || scr.type.toLowerCase() === type__Js) scripts.push(scr); });
}
else {
var children_nodes = body_el.childNodes, child;
for (i = 0; children_nodes[i]; i++) {
child = children_nodes[i];
if (
child.nodeName
&&
child.nodeName.toUpperCase() === tagName__Script__Upper
&&
(
!child.type
||
child.type.toLowerCase() === type__Js
)
) {
scripts.push(child);
}
// Recursive call
window.exec_body_scripts(child);
}
}
for (i = 0; scripts[i]; i++) {
evalScript(scripts[i]);
}
};
I had also pages that needed to execute javascript code (with eval) after the javascript files are loaded so did put load events on the script createelement so when the javascript file(s) were loaded gets executed. My website is a MVC SPA application. It loads partial pages with ajax. Those partial pages are set with innerhtml and then the javascript files are loaded and the javascript code on thst partial page.
Try function eval().
data.newScript = '<script type="text/javascript">//my script...</script>'
var element = document.getElementById('elementToRefresh');
element.innerHTML = data.newScript;
eval(element.firstChild.innerHTML);
This is a real example from a project that i am developing.
Thanks to this post

Categories