Script injected with innerHTML doesn't trigger onload and onerror - javascript

I am trying to bind onload and onerror events of script tag. This works fine when loading from src. Given the following function:
function injectJS(src, inline) {
var script = document.createElement("script");
if (inline) {
script.innerHTML = src;
} else {
script.src = src;
}
script.onload = function() {console.log("Success!");};
script.onerror = function() {console.log("Error!");};
document.body.appendChild(script);
}
I can easily know whether script has loaded:
> injectJS("https://code.jquery.com/jquery-3.1.1.min.js");
Success!
> injectJS("https://raw.githubusercontent.com/eligrey/FileSaver.js/master/FileSaver.js");
Error!
But when injecting inline JS, with innerHTML, the script doesn't fire the events:
> injectJS("console.log(\"Yes!\");", true);
Yes!
> injectJS("console.log(\"Loaded but error...\"); error;", true);
Loaded but error...
Success! hasn't been logged in these cases. However, I could just prepend some code that can call a function, for example, once the script is loaded.
The problem comes when there is an error that prevents script from loading in the first place, for example a syntax error:
> injectJS("console.log(\"Success! Or not..\"); syntax error;", true);
Nothing is logged (except the error, of course). How can I detect whether an injected script has loaded or errored out before loading?
Edit:
Oriol's answer has pointed me in the right direction. For reference, here is the final function that works exactly as I wanted and passes the 5 test cases:
function injectJS(src, inline, on_success, on_error) {
var script = document.createElement("script");
script.onload = on_success;
script.onerror = on_error;
if (inline) {
script.innerHTML = "window.script_loaded = true; " + src;
} else {
script.src = src;
}
document.body.appendChild(script);
if (inline) {
var loaded = window["script_loaded"];
window.script_loaded = false;
if (loaded) {
on_success();
} else {
on_error();
}
}
}

Inline scripts are loaded synchronously. So you don't need the events at all.
Just use
function injectJS(src, inline) {
var script = document.createElement("script");
script.onload = function() {console.log("Success!");};
script.onerror = function() {console.log("Error!");};
if (inline) {
script.innerHTML = src;
script.onload();
} else {
script.src = src;
}
document.body.appendChild(script);
}
injectJS("console.log(\"Yes!\");", true);
You won't be able to detect syntax errors, but it's just like with external scripts. The error event only implies the script couldn't be downloaded, not syntax errors.

Related

Use jQuery without Script tag in head

So I want to make jQuery load inside my script. the user doesn't have to add the <script> to the head, but the script adds automatically. I tried using:
(function() {;
var script = document.createElement("SCRIPT");
script.src = '/src/jquery-embed.js';
script.type = 'text/javascript';
document.getElementsByTagName("head")[0].appendChild(script);
var checkReady = function(callback) {
if (window.jQuery) {
callback(jQuery);
} else {
window.setTimeout(function() {
checkReady(callback);
}, 20);
}
};
checkReady(function($) {
//code here
fucntion callbutton {
console.log("i return as not defined when called by a button")
}
when I add it, it works fine but all my functions break. How can I go about this without screwing up more?
edit: Link to the file im working on here
Per discussion, to achieve your goal you must ask your clients to delay their script execution until you've loaded your libraries (jQuery in this case).
Shown below is all that is necessary to accomplish the loading:
var script = document.createElement('SCRIPT');
document.getElementsByTagName('head')[0].appendChild(script);
script.src = 'https://code.jquery.com/jquery-3.5.1.min.js';
script.onload = () => {
document.dispatchEvent(new CustomEvent('doYourThing'));
};
Then, your users would use this code:
document.addEventListener('doYourThing', ()=>{
// End user code that relies on jQuery goes here
});
Here is a plunkr that demonstrates the principle.
You forgot document.body.appendChild() and you also forgot to add the parentheses after callbutton
change it to this:
(function() {
var script = document.createElement("SCRIPT");
document.body.appendChild(script);
script.src = '/src/jquery-embed.js';
script.type = 'text/javascript';
document.getElementsByTagName("head")[0].appendChild(script);
var checkReady = function(callback) {
if (window.jQuery) {
callback(jQuery);
} else {
window.setTimeout(function() {
checkReady(callback);
}, 20);
}
};
checkReady(function($) {
//code here
function callbutton(){
console.log("i return as not defined when called by a button")
}
}
})

Callback in JavaScript

I'm studying about callbacks and other stuff like this, and in this
book(https://javascript.info/callbacks) there was example with this code
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(script);
document.head.append(script);
}
loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => {
alert(`Cool, the script ${script.src} is loaded`);
alert( _ ); // function declared in the loaded script
});
It appends to the document the new, dynamically created, tag <script src="…"> with given src.
The browser automatically starts loading it and executes when complete. I have a question:
Do I understand it right, that it should work with any kind of links, and get(obtain) functions and their results on that links?
And in this example the second alert should show some sort of result of that functions?
If so, how to insert in src another link with code? I've tried many other links, but second alert didn't work. For example:
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(script);
document.head.append(script);
}
loadScript('https://jsfiddle.net/Rom28/7rL28mso/', script => {
alert(`Cool, the script ${script.src} is loaded`);
alert(test()); // function declared in the loaded script
});
Please clarify it to me.
Also I want to ask what does null mean in this code:
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(**null**, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
It's the same code as above but it also has functionality for handling errors.
src parameter should contain link to js file (jsfiddle link doesn't contain js code only so it won't work). For instance you can try to create script.js file with test function in it and put loadScript function in your html file:
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(script);
document.head.append(script);
}
loadScript('script.js', script => {
alert(`Cool, the script ${script.src} is loaded`);
alert(test()); // function declared in the loaded script
});
So now you have access to the test function which is written in script.js

Listen to see if content from external js file is loaded in DOM for javascript to use

I'm trying to run javascript on a form that is loaded in content coming from an external js file, but it's failing because content doesn't load right away
I've tried listening for the load of the html button in the iframe, but not getting anything:
document.querySelector('#hyform button').addEventListener('load', function() {
console.log('external js content loaded');
}
UPDATED QUESTION. This is not an iframe but content loaded from an external js file. I dont have control of the external js file.
You can dynamically append the file to <head>, then monitor the load state:
var callback = function () {
// do stuff
};
var script = document.createElement('script');
script.src = 'path_to_file.js'
script.type = 'text/javascript';
if (script.readyState) {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
callback();
script.onreadystatechange = null;
}
} else {
script.onload = function () {
callback();
};
}
document.getElementsByTagName("head")[0].appendChild(script);

Cannot call function from dynamically loaded javascript file

I'm loading a javascript external file from another javascript file present in the document and since its loaded, I want to call a function from the loaded js file.
Here is the load function:
function loadScript(url) {
var head = window.top.document.getElementsByTagName('head')[0];
var script = window.top.document.createElement('script');
script.src = url;
script.type= "text/javascript";
head.appendChild(script);
if(script.readyState) { //IE
script.onreadystatechange = function() {
if ( script.readyState === "loaded" || script.readyState === "complete" ) {
script.onreadystatechange = null;
console.log("[BANDEAU] script loaded");
testAlert();
}
};
} else { //Others
script.onload = function() {
console.log("[BANDEAU] script loaded");
testAlert();
};
}
}
So it works nice because the javascript file is succesfuly loaded but I cannot access the testAlert() method from the loaded javascript file, as I try in the code above, right after printing that the script is loaded. When I try to get the type of the function with typeOf on window[testAlert], I get an undefined. But when I try to execute the testAlert() method in the developer console, it works perfectly. Does anyone see what I'm doing wrong ?
Does the position in the DOM between the caller javascript file and the loaded javascript file might be the reason ?
You need to assign the load handlers BEFORE changing the src
function loadScript(url) {
var head = document.getElementsByTagName('head')[0]; // window.top in frames/iFrames
var script = document.createElement('script');
script.type = "text/javascript";
if (script.readyState) { //IE
script.onreadystatechange = function() {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
console.log("[BANDEAU] script loaded");
testAlert(); // window.top.testAlert() if needed
}
};
}
else {
script.onload = function() {
console.log("[BANDEAU] script loaded");
testAlert(); // window.top.testAlert() if needed
};
}
script.src = url;
head.appendChild(script);
}
In addition to what mplungjan said, I'm pretty sure you'd have to do an eval() on the loaded script in order to have a legitimate address for the call to testAlert().
Also, check out this link for more info.

Dynamically added Javascript won't reload

I have a web page with a Javascript file added dynamically.
After changing the script (adding allert or smth like this) I reload the page, and push a trigger button for adding the script, but the browser uses the old one (chached).
Tried it in chrome and IE.
Other scripts (that are not added dynamically) reload well.
Here is the function that loads the script:
function addScript (s)
{
script = document.createElement('script');
script.type = 'text/javascript';
script.src = s;
document.getElementsByTagName('head')[0].appendChild(script);
script.onload=function ()
{
switch (s)
{
case 'some address':
functionInTheNewFile(); break;
default: break;
}
};
}
What is wrong here?
If it is an external script caching, append the current date and time to the end of the script. IE:
var nowDate = new Date();
script.src = s + "?nocache=" + nowDate.getTime();
IE do not fire the load event, you have to use attachEvent instead. And always add the eventlistener before you locate the script.
script = document.createElement('script');
script.type = 'text/javascript';
if(script.attachEvent) {
script.attachEvent('onreadystatechange', callback);
} else {
script.onload = callback;
}
script.src = s;

Categories

Resources