Callback in JavaScript - 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

Related

Activating alternate <script> tags [duplicate]

I want to dynamically include a script tag in a webpage however I have no control of it's src so src="source.js" may look like this.
document.write('<script type="text/javascript">')
document.write('alert("hello world")')
document.write('</script>')
document.write('<p>goodbye world</p>')
Now ordinarily putting
<script type="text/javascript" src="source.js"></script>
In the head works fine but is there any other way I can add source.js dynamically using something like innerHTML?
jsfiddle of what i've tried
var my_awesome_script = document.createElement('script');
my_awesome_script.setAttribute('src','http://example.com/site.js');
document.head.appendChild(my_awesome_script);
You can use the document.createElement() function like this:
function addScript( src ) {
var s = document.createElement( 'script' );
s.setAttribute( 'src', src );
document.body.appendChild( s );
}
There is the onload function, that could be called when the script has loaded successfully:
function addScript( src, callback ) {
var s = document.createElement( 'script' );
s.setAttribute( 'src', src );
s.onload=callback;
document.body.appendChild( s );
}
It's almost a decade later and nobody bothers to write the Promise version, so here is mine (based on this awnser):
function addScript(src) {
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.setAttribute('src', src);
s.addEventListener('load', resolve);
s.addEventListener('error', reject);
document.body.appendChild(s);
});
}
Usage
try {
await addScript('https://api.stackexchange.com/js/2.0/all.js');
// do something after it was loaded
} catch (e) {
console.log(e);
}
a nice little script I wrote to load multiple scripts:
function scriptLoader(scripts, callback) {
var count = scripts.length;
function urlCallback(url) {
return function () {
console.log(url + ' was loaded (' + --count + ' more scripts remaining).');
if (count < 1) {
callback();
}
};
}
function loadScript(url) {
var s = document.createElement('script');
s.setAttribute('src', url);
s.onload = urlCallback(url);
document.head.appendChild(s);
}
for (var script of scripts) {
loadScript(script);
}
};
usage:
scriptLoader(['a.js','b.js'], function() {
// use code from a.js or b.js
});
When scripts are loaded asynchronously they cannot call document.write. The calls will simply be ignored and a warning will be written to the console.
You can use the following code to load the script dynamically:
var scriptElm = document.createElement('script');
scriptElm.src = 'source.js';
document.body.appendChild(scriptElm);
This approach works well only when your source belongs to a separate file.
But if you have source code as inline functions which you want to load dynamically and want to add other attributes to the script tag, e.g. class, type, etc., then the following snippet would help you:
var scriptElm = document.createElement('script');
scriptElm.setAttribute('class', 'class-name');
var inlineCode = document.createTextNode('alert("hello world")');
scriptElm.appendChild(inlineCode);
document.body.appendChild(scriptElm);
You can try following code snippet.
function addScript(attribute, text, callback) {
var s = document.createElement('script');
for (var attr in attribute) {
s.setAttribute(attr, attribute[attr] ? attribute[attr] : null)
}
s.innerHTML = text;
s.onload = callback;
document.body.appendChild(s);
}
addScript({
src: 'https://www.google.com',
type: 'text/javascript',
async: null
}, '<div>innerHTML</div>', function(){});
A one-liner (no essential difference to the answers above though):
document.body.appendChild(document.createElement('script')).src = 'source.js';
This Is Work For Me.
You Can Check It.
var script_tag = document.createElement('script');
script_tag.setAttribute('src','https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js');
document.head.appendChild(script_tag);
window.onload = function() {
if (window.jQuery) {
// jQuery is loaded
alert("ADD SCRIPT TAG ON HEAD!");
} else {
// jQuery is not loaded
alert("DOESN'T ADD SCRIPT TAG ON HEAD");
}
}
Loads scripts that depends on one another with the right order.
Based on Satyam Pathak response, but fixed the onload.
It was triggered before the script actually loaded.
const scripts = ['https://www.gstatic.com/firebasejs/6.2.0/firebase-storage.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-firestore.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js']
let count = 0
const recursivelyAddScript = (script, cb) => {
const el = document.createElement('script')
el.src = script
if(count < scripts.length) {
count ++
el.onload = () => recursivelyAddScript(scripts[count])
document.body.appendChild(el)
} else {
console.log('All script loaded')
return
}
}
recursivelyAddScript(scripts[count])
Well, there are multiple ways you can include dynamic javascript,
I use this one for many of the projects.
var script = document.createElement("script")
script.type = "text/javascript";
//Chrome,Firefox, Opera, Safari 3+
script.onload = function(){
console.log("Script is loaded");
};
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);
You can call create a universal function which can help you to load as many javascript files as needed. There is a full tutorial about this here.
Inserting Dynamic Javascript the right way
No one mentioned it, but you can also stick the actual source code into a script tag by making a URL out of it using URL and Blob:
const jsCode = `
// JS code in here. Maybe you extracted it from some HTML string.
`
const url = URL.createObjectURL(new Blob([jsCode]))
const script = document.createElement('script')
script.src = url
URL.revokeObjectURL(url) // dispose of it when done
as for the jsCode, you may have gotten it from some HTML.
Here's a more full example of how you'd handle any number of scripts in an HTML source:
main()
async function main() {
const scriptTagOpen = /<script\b[^>]*>/g
const scriptTagClose = /<\/script\b[^>]*>/g
const scriptTagRegex = /<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>/g
const response = await fetch('path/to/some.html')
const html = await response.text()
someElement.innerHTML = html
// We need to get the script tags and manually add them to DOM
// because otherwise innerHTML will not execute them.
const codes =
html
.match(scriptTagRegex)
?.map(code => code.replace(scriptTagOpen, '').replace(scriptTagClose, ''))
.map(code => URL.createObjectURL(new Blob([code]))) || []
for (const code of codes) {
const script = document.createElement('script')
script.src = code
someElement.append(script)
URL.revokeObjectURL(code)
}
}
the only way to do this is to replace document.write with your own function which will append elements to the bottom of your page. It is pretty straight forward with jQuery:
document.write = function(htmlToWrite) {
$(htmlToWrite).appendTo('body');
}
If you have html coming to document.write in chunks like the question example you'll need to buffer the htmlToWrite segments. Maybe something like this:
document.write = (function() {
var buffer = "";
var timer;
return function(htmlPieceToWrite) {
buffer += htmlPieceToWrite;
clearTimeout(timer);
timer = setTimeout(function() {
$(buffer).appendTo('body');
buffer = "";
}, 0)
}
})()
I tried it by recursively appending each script
Note If your scripts are dependent one after other, then position will need to be in sync.
Major Dependency should be in last in array so that initial scripts can use it
const scripts = ['https://www.gstatic.com/firebasejs/6.2.0/firebase-storage.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-firestore.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js']
let count = 0
const recursivelyAddScript = (script, cb) => {
const el = document.createElement('script')
el.src = script
if(count < scripts.length) {
count ++
el.onload = recursivelyAddScript(scripts[count])
document.body.appendChild(el)
} else {
console.log('All script loaded')
return
}
}
recursivelyAddScript(scripts[count])
Here is a minified snippet, same code as Google Analytics and Facebook Pixel uses:
!function(e,s,t){(t=e.createElement(s)).async=!0,t.src="https://example.com/foo.js",(e=e.getElementsByTagName(s)[0]).parentNode.insertBefore(t,e)}(document,"script");
Replace https://example.com/foo.js with your script path.
window.addEventListener("load", init);
const loadScript = async (url) => {
const response = await fetch(url);
const script = await response.text();
eval(script);
}
function init() {
const wistiaVideo = document.querySelector(".wistia_embed");
if ("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype) {
let lazyVideoObserver = new IntersectionObserver(function (entries, observer) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
setTimeout(() => loadScript("//fast.wistia.com/assets/external/E-v1.js"), 1000);
lazyVideoObserver.unobserve(entry.target);
console.log("E-v1.js script loaded from fast.wistia.com");
}
});
});
lazyVideoObserver.observe(wistiaVideo);
}
}
<div style="height: 150vh; background-color: #f7f7f7;"></div>
<h1>Wistia Video!</h1>
<div class="wistia_embed wistia_async_29b0fbf547" style="width:640px;height:360px;"> </div>
<h1>Video Ended!</h1>

'$ is not defined' on first load

I'm writing a html plugin for a tool(sonarqube).
In this I need to write code in below fashion by first registering a extension.
While running the code, I'm facing:
ReferenceError: $ is not defined
Code:
window.registerExtension('CustomPlugin/muPlugin', function (options) {
script = document.createElement('script');
script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js';
document.head.appendChild(script);
var pluginContainer = document.createElement('div');
pluginContainer.setAttribute("id", "pluginContainer");
options.el.appendChild(pluginContainer)
$("#pluginContainer").load("/static/CustomPlugin/customPluginWebPage.html"); // Facing error on this line.
return function () {};
});
It works when I load the plugin second time, but not the first time.
Any suggestion, how can I make sure jquery is available the first time?
Thanks you
Possible duplication of - document.createElement(“script”) synchronously
ES5:
You can create your element with an "onload" handler, and that will be called when the script has been loaded and evaluated by the browser.
script = document.createElement('script');
script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js';
//Bind a onload handler
script.onload = () => {
console.log($);
};
document.head.appendChild(script);
EDIT 1:
ES6:
The above is the best solution, unless you're prepared to host jQuery locally then you could use dynamic import() which runs asynchronously. Support is not great - https://caniuse.com/#feat=es6-module-dynamic-import. Here is another link making use of this. I would only recommend using this where BabelJS is used.
import('./jquery.min.js').then((jquery) => {
window.jQuery = jquery;
window.$ = jquery;
// The rest of your code
});
Try using setTimeOut()
window.registerExtension('CustomPlugin/muPlugin', function (options) {
script = document.createElement('script');
script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js';
document.head.appendChild(script);
var pluginContainer = document.createElement('div');
pluginContainer.setAttribute("id", "pluginContainer");
options.el.appendChild(pluginContainer);
setTimeout(() => {
$("#pluginContainer").load("/static/CustomPlugin/customPluginWebPage.html");
}, 2000);
return function () {};
});

Script injected with innerHTML doesn't trigger onload and onerror

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.

add dynamic version in link and script tag using javascript [duplicate]

I want to dynamically include a script tag in a webpage however I have no control of it's src so src="source.js" may look like this.
document.write('<script type="text/javascript">')
document.write('alert("hello world")')
document.write('</script>')
document.write('<p>goodbye world</p>')
Now ordinarily putting
<script type="text/javascript" src="source.js"></script>
In the head works fine but is there any other way I can add source.js dynamically using something like innerHTML?
jsfiddle of what i've tried
var my_awesome_script = document.createElement('script');
my_awesome_script.setAttribute('src','http://example.com/site.js');
document.head.appendChild(my_awesome_script);
You can use the document.createElement() function like this:
function addScript( src ) {
var s = document.createElement( 'script' );
s.setAttribute( 'src', src );
document.body.appendChild( s );
}
There is the onload function, that could be called when the script has loaded successfully:
function addScript( src, callback ) {
var s = document.createElement( 'script' );
s.setAttribute( 'src', src );
s.onload=callback;
document.body.appendChild( s );
}
It's almost a decade later and nobody bothers to write the Promise version, so here is mine (based on this awnser):
function addScript(src) {
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.setAttribute('src', src);
s.addEventListener('load', resolve);
s.addEventListener('error', reject);
document.body.appendChild(s);
});
}
Usage
try {
await addScript('https://api.stackexchange.com/js/2.0/all.js');
// do something after it was loaded
} catch (e) {
console.log(e);
}
a nice little script I wrote to load multiple scripts:
function scriptLoader(scripts, callback) {
var count = scripts.length;
function urlCallback(url) {
return function () {
console.log(url + ' was loaded (' + --count + ' more scripts remaining).');
if (count < 1) {
callback();
}
};
}
function loadScript(url) {
var s = document.createElement('script');
s.setAttribute('src', url);
s.onload = urlCallback(url);
document.head.appendChild(s);
}
for (var script of scripts) {
loadScript(script);
}
};
usage:
scriptLoader(['a.js','b.js'], function() {
// use code from a.js or b.js
});
When scripts are loaded asynchronously they cannot call document.write. The calls will simply be ignored and a warning will be written to the console.
You can use the following code to load the script dynamically:
var scriptElm = document.createElement('script');
scriptElm.src = 'source.js';
document.body.appendChild(scriptElm);
This approach works well only when your source belongs to a separate file.
But if you have source code as inline functions which you want to load dynamically and want to add other attributes to the script tag, e.g. class, type, etc., then the following snippet would help you:
var scriptElm = document.createElement('script');
scriptElm.setAttribute('class', 'class-name');
var inlineCode = document.createTextNode('alert("hello world")');
scriptElm.appendChild(inlineCode);
document.body.appendChild(scriptElm);
You can try following code snippet.
function addScript(attribute, text, callback) {
var s = document.createElement('script');
for (var attr in attribute) {
s.setAttribute(attr, attribute[attr] ? attribute[attr] : null)
}
s.innerHTML = text;
s.onload = callback;
document.body.appendChild(s);
}
addScript({
src: 'https://www.google.com',
type: 'text/javascript',
async: null
}, '<div>innerHTML</div>', function(){});
A one-liner (no essential difference to the answers above though):
document.body.appendChild(document.createElement('script')).src = 'source.js';
This Is Work For Me.
You Can Check It.
var script_tag = document.createElement('script');
script_tag.setAttribute('src','https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js');
document.head.appendChild(script_tag);
window.onload = function() {
if (window.jQuery) {
// jQuery is loaded
alert("ADD SCRIPT TAG ON HEAD!");
} else {
// jQuery is not loaded
alert("DOESN'T ADD SCRIPT TAG ON HEAD");
}
}
Loads scripts that depends on one another with the right order.
Based on Satyam Pathak response, but fixed the onload.
It was triggered before the script actually loaded.
const scripts = ['https://www.gstatic.com/firebasejs/6.2.0/firebase-storage.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-firestore.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js']
let count = 0
const recursivelyAddScript = (script, cb) => {
const el = document.createElement('script')
el.src = script
if(count < scripts.length) {
count ++
el.onload = () => recursivelyAddScript(scripts[count])
document.body.appendChild(el)
} else {
console.log('All script loaded')
return
}
}
recursivelyAddScript(scripts[count])
Well, there are multiple ways you can include dynamic javascript,
I use this one for many of the projects.
var script = document.createElement("script")
script.type = "text/javascript";
//Chrome,Firefox, Opera, Safari 3+
script.onload = function(){
console.log("Script is loaded");
};
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);
You can call create a universal function which can help you to load as many javascript files as needed. There is a full tutorial about this here.
Inserting Dynamic Javascript the right way
No one mentioned it, but you can also stick the actual source code into a script tag by making a URL out of it using URL and Blob:
const jsCode = `
// JS code in here. Maybe you extracted it from some HTML string.
`
const url = URL.createObjectURL(new Blob([jsCode]))
const script = document.createElement('script')
script.src = url
URL.revokeObjectURL(url) // dispose of it when done
as for the jsCode, you may have gotten it from some HTML.
Here's a more full example of how you'd handle any number of scripts in an HTML source:
main()
async function main() {
const scriptTagOpen = /<script\b[^>]*>/g
const scriptTagClose = /<\/script\b[^>]*>/g
const scriptTagRegex = /<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>/g
const response = await fetch('path/to/some.html')
const html = await response.text()
someElement.innerHTML = html
// We need to get the script tags and manually add them to DOM
// because otherwise innerHTML will not execute them.
const codes =
html
.match(scriptTagRegex)
?.map(code => code.replace(scriptTagOpen, '').replace(scriptTagClose, ''))
.map(code => URL.createObjectURL(new Blob([code]))) || []
for (const code of codes) {
const script = document.createElement('script')
script.src = code
someElement.append(script)
URL.revokeObjectURL(code)
}
}
the only way to do this is to replace document.write with your own function which will append elements to the bottom of your page. It is pretty straight forward with jQuery:
document.write = function(htmlToWrite) {
$(htmlToWrite).appendTo('body');
}
If you have html coming to document.write in chunks like the question example you'll need to buffer the htmlToWrite segments. Maybe something like this:
document.write = (function() {
var buffer = "";
var timer;
return function(htmlPieceToWrite) {
buffer += htmlPieceToWrite;
clearTimeout(timer);
timer = setTimeout(function() {
$(buffer).appendTo('body');
buffer = "";
}, 0)
}
})()
I tried it by recursively appending each script
Note If your scripts are dependent one after other, then position will need to be in sync.
Major Dependency should be in last in array so that initial scripts can use it
const scripts = ['https://www.gstatic.com/firebasejs/6.2.0/firebase-storage.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-firestore.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js']
let count = 0
const recursivelyAddScript = (script, cb) => {
const el = document.createElement('script')
el.src = script
if(count < scripts.length) {
count ++
el.onload = recursivelyAddScript(scripts[count])
document.body.appendChild(el)
} else {
console.log('All script loaded')
return
}
}
recursivelyAddScript(scripts[count])
Here is a minified snippet, same code as Google Analytics and Facebook Pixel uses:
!function(e,s,t){(t=e.createElement(s)).async=!0,t.src="https://example.com/foo.js",(e=e.getElementsByTagName(s)[0]).parentNode.insertBefore(t,e)}(document,"script");
Replace https://example.com/foo.js with your script path.
window.addEventListener("load", init);
const loadScript = async (url) => {
const response = await fetch(url);
const script = await response.text();
eval(script);
}
function init() {
const wistiaVideo = document.querySelector(".wistia_embed");
if ("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype) {
let lazyVideoObserver = new IntersectionObserver(function (entries, observer) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
setTimeout(() => loadScript("//fast.wistia.com/assets/external/E-v1.js"), 1000);
lazyVideoObserver.unobserve(entry.target);
console.log("E-v1.js script loaded from fast.wistia.com");
}
});
});
lazyVideoObserver.observe(wistiaVideo);
}
}
<div style="height: 150vh; background-color: #f7f7f7;"></div>
<h1>Wistia Video!</h1>
<div class="wistia_embed wistia_async_29b0fbf547" style="width:640px;height:360px;"> </div>
<h1>Video Ended!</h1>

Delay script loading

So if I have the following:
<script type="text/javascript" src="offsite file I am referencing"></script>
and I simply want to delay the execution of calling that file using settimeout, how would I go about that?
Very strange in that I would have no problem using settimeout on a simple function, but I am kind of stumped in this seemingly more simple situation.
My thought would be I could just make a function that calls that file after x amount of time, but calling the file in the function seems to be escaping me.
you are almost there.
in your settimeout callback function do the following:
var script = document.createElement('script');
script.src = "http://whatever.com/the/script.js";
document.getElementsByTagName('head')[0].appendChild(script);
The simplest way would be to let the script file load normally and just call a main function in it with setTimeout() like this:
<script type="text/javascript" src="offsite file I am referencing"></script>
<script type="text/javascript">
setTimeout(executeMainFunction, 5000); // function in offsite js file
</script>
If you cannot do that for some reason, then you can delay the loading of the external script file like this:
setTimeout(function() {
var headID = document.getElementsByTagName("head")[0];
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'http://www.somedomain.com/somescript.js';
headID.appendChild(newScript);
}, 5000);
Here's a reference article on dynamic loading of script files (and other types of resources): http://www.hunlock.com/blogs/Howto_Dynamically_Insert_Javascript_And_CSS.
You can use DOM manipulation to create a new script tag at runtime. Adding it into the document will load the external JS file just as if you had written it into the HTML in the first place.
var loadScript = function(sourceSrc){
var scriptTag = document.createElement('script');
scriptTag.src = scriptSrc;
document.getElementsByTagName('head')[0].appendChild(scriptTag);
}
You can delay the script from loading, until the page finishes loading, using the HTML script defer attribute:
<script src="offsite file I am referencing" defer></script>
If the purpose of this exercise is to delay the loading of external resources to simulate potential real life scenarios (e.g. when loading 3rd party widgets, etc), then I'd go down a very different route.
The following are two different delay proxy implementations that can be used to simulate and test unexpected network conditions:
http://www.deelay.me/
https://www.npmjs.com/package/grunt-connect-delay
They both work by using a prefix like /delay/5000/ to specify the delay simulation period.
Mozilla Developer Network explains various approaches:
MDN Async Script Techniques
<script async src="file.js"></script>
or
var script = document.createElement('script');
script.src = "file.js";
document.body.appendChild(script);
or if your JavaScript is in a String:
var blob = new Blob([codeString]);
var script = document.createElement('script');
var url = URL.createObjectURL(blob);
script.onload = script.onerror = function() { URL.revokeObjectURL(url); };
script.src = url;
document.body.appendChild(script);
There is also good information when async is not async as well as how to get around those cases.
I have created for ReactJS and its worked for me.
1. File: widget.js with promise:
const delayTime = 20000; // loading 20sec delay.
const loadJS = async () => {
return await new Promise(function (resolve, reject) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = 'https://yoururl.com/js/widget.js';
script.onload = resolve;
script.onerror = () => {
reject('Cannot load js')
document.head.removeChild(script);
}
document.head.appendChild(script);
}) }
function initLoadJS() {
loadJS()
.then(()=> console.log('testing'))
.catch((error)=>console.error(error)) }
function delayLoadingJS() {
setTimeout((event)=>initLoadJS(event), delayTime);
}
export default delayLoadingJS;
2. Calling delayLoadingJS() function on the page:
When page loading completed then after 20 sec later initLoadJS() method will trigger and it attach the 3rd party javascript file(https://yoururl.com/js/widget.js) on page.
componentDidUpdate(prevProps, prevState) {
if (this.state.page !== prevState.page) {
delayLoadingJS();
}
}
For a NextJS cript, the code below will work fine:
<script id="sample_id">
setTimeout(function(){
var script = document.createElement('script');
script.src = "https://link_to_load";
document.getElementsByTagName('head')[0].appendChild(script);
},
4000);
</script>

Categories

Resources