Reputation: 21304
I'm creating a script tag dynamically:
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.defer = true;
script.async = true;
script.text = 'some my javascript content here';
head.appendChild(script);
script.onload = function () {
// this never get fired..
debugger;
}
How to get notified when script was executed inside other code block? Maybe some event?
Upvotes: 14
Views: 15015
Reputation: 7350
From what I found, you can simply do this:
var script = document.createElement("script");
script.innerHTML = "console.log('(1) hello from inline script');"
document.head.appendChild(script);
setTimeout(
function() {
console.log("(2) we're done!");
},
0 // 👈 zero timeout
);
Why this works: The zero timeout means basically "next event cycle" - and since it's an inline <script>
node, there's no delay from any network activity when you append it to <head>
, which means the browser will have executed the inline script immediately after the next event-cycle.
Just don't use the load
event, and you're good to go.
Tested and worked in IE 10 + 11 and current versions of Chrome, Firefox, Edge, Opera and Safari.
If your script needs to support both inline and external scripts, just use a condition like if (script.src)
to test if the script is external - then conditionally use a load
event listener or the zero timeout.
Upvotes: 0
Reputation: 13703
The code checks every 100ms if the script got appended to the DOM. You can use it anywhere across your app without the need of event listeners and dispatch events. Likewise, you can see a time interval for which the code will throw an error if the script doesn't append in the time interval that you've set.
const waitForScriptToLoad = (scriptName, checkTimeMs, timeOutMs) => {
let elapsedTime = 0;
return new Promise((resolve, reject) => {
setTimeout(x => reject('script: ' + scriptName + ' Timed out!')
, timeOutMs)
const time = setInterval(() => {
elapsedTime += checkTimeMs;
if (document.body.innerHTML.indexOf(scriptName) > -1) {
resolve(
{
response: 'script: ' + scriptName + ' found!',
time: (elapsedTime / 1000).toFixed(2) + 's'
});
clearInterval(time);
}
}, checkTimeMs)
})
}
waitForScriptToLoad('script_name.js', 100, 20000)
.then(res => console.log(res))
.catch(err => console.log(err))
Upvotes: 0
Reputation: 17703
I was able to get this to work by adding an ID to the script, then in the JS, manually firing the load event on that DOM element. Tested only in Chrome, will have issues in older IE according to MDN).
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.id = 'testing';
script.defer = true;
script.async = true;
script.onload = function () {
console.log('The script is loaded');
}
script.text = ["console.log('This is from the script');",
"var script = document.getElementById('testing');",
"var event = new UIEvent('load');",
"script.dispatchEvent(event);"].join('');
head.appendChild(script);
Upvotes: 10
Reputation: 786
For testing purposes you can always add a call to the alert() function. However, after looking at your code I'm not sure I see anything in it to actually do the calling of the function. That "onload" event handler probably should be added to the dynamic creation section, before you do the appendChild().
I wrote some code a while back to do some dynamic script creation, and it works fine. My code has two main functions, one that creates the scripts (a bunch of them), loading data from ".js" files, and another that calls the functions in those scripts, in a sequence determined by their names (using the "eval()" function). Your function doesn't have any name that I can see... I know that a lot of folks frown on using the eval() function, but so long as the only thing being called by it is something YOU wrote in its entirety, it should be fine.
This code lets the BROWSER, not the Web Server, create a menu of clickable-link items dynamically (each clickable link looks like an ordinary hyperlink, but is actually a JavaScript construct, and the browser has to have JavaScript enabled for the link to work --but then it had to have JavaScript enabled to create the menu, so no problem!):
var F00, F01, F02, F03, F04, F05, F06, F07, F08, F09,
F10, F11, F12, F13, F14, F15, F16, F17, F18, F19;
var dat = new Array();
var form, script, str, st2, tmp, tmp2, dtno, indx, unde;
function initialize()
{ window.name="MyMenu";
form = document.getElementById('MENU');
for(indx=0; indx<20; indx++)
{ str = "0" + indx;
tmp = str.length - 2;
str = str.substr(tmp);
script = document.createElement('script');
script.type = 'text/javascript';
script.src = str + ".js";
form.appendChild(script);
}
window.setTimeout("BuildMenu();", 1000); //delay is necessary;
// scripts are actually only loaded after the function ends,
// and you need to allow time for it to finish
// before calling the functions in those scripts.
return;
}
Note this code is PREPARED to handle 20 menu items, even if you only have 5 items currently ready to be on the menu. The above function doesn't crash if some of the 20 maximum ".js" files don't exist.
function BuildMenu()
{ dtno = 0;
for(indx=0; indx<20; indx++)
{ str = "0" + indx;
tmp = str.length - 2;
str = "F" + str.substr(tmp);
tmp = eval(str);
if(tmp != unde) //no value is assigned to 'unde'; it is undefined;
//this is a valid way to find out
//whether or not a ".js" script existed/was-loaded.
dat[dtno++] = eval(str + "()");
}
dat.sort();
for(indx=0; indx<dtno; indx++)
{ str = "0" + indx;
tmp = str.length - 2;
str = "W" + str.substr(tmp);
tmp = document.getElementById(str);
tmp.innerHTML = "<a onclick=\"window.open('" + dat[indx][1] + "', 'MyMenu');\" style=\"color:#0000ff;text-decoration:underline;cursor:pointer;\">" + dat[indx][0] + "</a> " + dat[indx][2] + "<br />";
}
return;
}
In the HTML code for the web page, there is this:
<body onload="initialize();>
<br />
<form id="MENU" action="" onsubmit="return false;">
<span id="W00"> </span>
<span id="W01"> </span>
<span id="W02"> </span>
<span id="W03"> </span>
<span id="W04"> </span>
<span id="W05"> </span>
<span id="W06"> </span>
<span id="W07"> </span>
<span id="W08"> </span>
<span id="W09"> </span>
<span id="W10"> </span>
<span id="W11"> </span>
<span id="W12"> </span>
<span id="W13"> </span>
<span id="W14"> </span>
<span id="W15"> </span>
<span id="W16"> </span>
<span id="W17"> </span>
<span id="W18"> </span>
<span id="W19"> </span>
</form>
Here is some sample code for the "00.js" file:
<!--
function F00()
{ return ["Menu Item Alpha", "./Alpha.htm", "Select Web Page Alpha"];
}
-->
Note the function simply returns 3 array-elements (strings). The BuildMenu() function uses those strings in constructing the menu on the web page, partly by modifying the innerHTML of the span elements.
Upvotes: -2
Reputation: 23396
In modern browsers you could use Mutation Observer to detect changes in an element – head
in this case. Something like this:
observer = new MutationObserver(function (m) {
// This will be fired
});
observer.observe(document.head, {childList: true});
Unfortenately this doesn't work in IE < 11, but it seems that onload
is fired in IE, so you can use it for IEs.
Upvotes: 3
Reputation: 22395
You need to define the onload
function before you set the script source. However, as Teemu has told me, since you are writing the javascript via the text
property, the onload
event never fires. Your best option would be to have an external js file and load it via the src
attribute.
The order should be:
- Append script element to DOM
- Define onload function
- define
src
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.defer = true;
script.async = true;
head.appendChild(script);
script.onload = function () {
// this never get fired..
debugger;
}
script.src = 'scriptName.js';
Then your onload event should fire, and you can insert a console.log("script has loaded!");
statement into the function.
Upvotes: -1
Reputation: 1976
Extract the code to a .js-file.
Add script.src = 'yourjs.js';
to the Script.
The .js-file is immerdently executed when the script is added to the DOM.
Add a if(console){console.debug('helloworld.');}
ontop of the yourjs.js
and you will see the message.
Upvotes: -1