Loading javascript asychronously with dependencies - javascript

I'm currently trying to load my javascript file with the following code
window.onload = function()
{
var arr = new Array();
arr[0] = "/js/main.js";
arr[1] = "/js/jquery.expander.min.js";
arr[2] = "/js/custom_expander.js";
for (var i = 0; i < arr.length; i++)
{
var tag = document.createElement("script");
tag.type = "text/javascript";
tag.async = true;
tag.src = arr[i];
document.getElementsByTagName("head")[0].appendChild(tag);
}
};
It's working well but my problem is that my third file (custom_expander.js) depends on the second (jquery.expander.min.js).
That's mostly why custom_expander.js is third in the list by the way.
However, even with this code, I have the following error
TypeError: $(...).expander is not a function
This error is here because my custom_expander.js loaded before jquery.expander.min.js finished loading. When the files are loaded synchronously like below, there's no error.
<script src="/js/main.js" type="text/javascript"></script>
<script src="/js/jquery.expander.min.js" type="text/javascript"></script>
<script src="/js/custom_expander.js" type="text/javascript"></script>
I had the exact same problem when I wanted to load jquery asynchronously and I've decided to keep it in my tag but it's not a solution.
How can I do to load javascript files asynchronously when there's dependencies between files ?

As I posted in above comments, if you are working with jQuery, you can use the $.getScript() method (jQuery Docs)
Here is an example (try it with your own scripts at jsfiddle):
var MyApp = {
Scripts: {
// put them with the correct order
ToLoad: [
'/path/to/script1.js',
'/path/to/script2.js',
'/path/to/script3.js'
],
Loaded: 0
},
DoTheJob: function(){
if( this.Scripts.ToLoad.length == this.Scripts.Loaded ) return;
$.getScript(this.Scripts.ToLoad[this.Scripts.Loaded], function(){
MyApp.Scripts.Loaded++;
MyApp.DoTheJob();
});
}
};
// do it on ready (faster way)
$(function(){
MyApp.DoTheJob();
});
// or do it on load
//window.onload = function(){
// MyApp.DoTheJob();
//});
Remember that you can improve it with a handler when all scripts have been loaded.

I suggest you to use RequireJs.It's cool thing.I think you will like it.it's not difficult

Related

How do I scripts work in HTML template on injection on the overriden page? (Importing jQuery)

Apparently, scripts in HTML template only work in HTML template itself, and not the page that the HTML template is injected into. (The scripts still execute, but they rely on jQuery, and even though its imported before the others, it spits out errors.)
To elaborate, here is my code:
function cleanDocument(names) {
var element = document.documentElement;
for (var i = element.attributes.length - 1; i >= 0; i--) {
element.removeAttribute(element.attributes[i].name);
}
for (var i = 0; i < names.length; i++) {
var elements = document.getElementsByTagName(names[i]);
if (elements.length === 0)
document.documentElement.appendChild(document.createElement(names[i]));
}
window.stop();
}
var documentElements = ['html'];
cleanDocument(documentElements);
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// Typical action to be performed when the document is ready:
var PBSKPage = xhttp.responseText;
document.querySelector('html').innerHTML = "";
loadPBSKPage();
function loadPBSKPage() {
document.querySelector('html').innerHTML = PBSKPage;
}
}
};
var actualpage = chrome.runtime.getURL('/2015/wildkratts/wk_homepage.html')
xhttp.open("GET", actualpage, true);
xhttp.send();
function insertAndExecute() {
var scripts = Array.prototype.slice.call(document.getElementsByTagName("script"));
var jquery = document.createElement("script");
jquery.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js";
document.head.appendChild(jquery);
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);
}
}
}
This code applies to https://pbskids.org/wildkratts/.
Basically, what it does is it wipes the document clean, and then, using XMLHttpRequest, it injects the HTML template inside the extension onto the page. However, all the scripts inside the HTML template require jQuery, and when accessing the overriden page (pbskids.org/wildkratts/), the scripts don't fully work due to uncaught errors (i.e. Uncaught ReferenceError: $ is not defined) that would be resolved if jQuery was imported.
I then accessed the HTML template URL itself, and what do you know, the scripts actually executed no problem.
Here's the order of my script tags in the HTML template:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/jquery_libraries/1.11.0/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/vendor/jquery-1.11.0.min.js"><\/script>')</script>
<script src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/vendor/jquery-1.11.0.min.js"></script>
<script src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/vendor/jquery.nivo.slider.pack.js" type="text/javascript"></script>
<script src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/vendor/jquery.touchSwipe.min.js" type="text/javascript"></script>
<script src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/vendor/retina.min.js"></script>
<script src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/plugins.js"></script>
<script type="text/javascript" src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/swfobject.js"></script>
<script type="text/javascript" src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/vendor/jquery.mobile.custom.min.js"></script>
<script type="text/javascript" src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/vendor/jquery.nivo.slider.3.2.plus.sliding.js"></script>
<script src="chrome-extension://niabfndainielhgpcjenbpodannhfofj/2015/pbsk_resources/wildkratts/js/pxaudio.min.js"></script>
<script type="text/javascript" src="https://www-tc.pbskids.org/includes/javascript/bridge.urls.js"></script>
<script type="text/javascript" src="https://www-tc.pbskids.org/includes/javascript/bridge.js"></script>
I would like to make it so that the scripts fuylly work correctly when on the overriden page itself, not the HTML template. I guess I could make it redirect to the HTML template, but I really don't want that.
When adding script elements individually via appendChild or similar DOM methods, each script with src is running asynchronously i.e. it doesn't wait for the previous script so it may run before jQuery runs. The solution is to wait for load event before running the next script:
async function insertAndExecute() {
for (const orig of document.querySelectorAll('script')) {
const copy = document.createElement('script');
copy.textContent = orig.textContent;
copy.src = orig.src;
orig.replaceWith(copy);
if (copy.src) {
await new Promise(resolve => copy.addEventListener('load', resolve, {once: true}));
}
}
}

Conditional loading of jQuery

I am testing with pure JavaScript if browser seems to support HTML5 and if so, I want to load jQuery and then process the rest of page. If not, some redirection will occur.
<script type="text/javascript">
var canvas = document.createElement('canvas');
if (canvas && canvas.getContext && canvas.getContext('2d')) {
var s = document.getElementsByTagName('script')[0];
var jq = document.createElement('script');
jq.type = 'text/javascript';
jq.src = 'js/jquery.js';
s.parentNode.insertBefore(jq, s);
}
else {
// ... redirection ...
}
</script>
<script type="text/javascript">
$(function () {
//...
}
</script>
But the code above is not working properly, because I got error
Uncaught ReferenceError: $ is not defined
which is clearly saying that jQuery library has not been loaded.
Why? What is wrong with conditional script loading in my code above?
This is a case where it may make sense to use document.write(). You'd need to put this code in the <body> instead of the <head>:
<script type="text/javascript">
var canvas = document.createElement('canvas');
if (canvas && canvas.getContext && canvas.getContext('2d')) {
document.write( '<script src="js/jquery.js"><\/script>' );
}
else {
// ... redirection ...
}
</script>
<script type="text/javascript">
$(function () {
//...
}
</script>
Or, you may be able to use an ordinary <script> tag to load jQuery, but put it after your conditional redirection:
<script>
var canvas = document.createElement('canvas');
if( !( canvas && canvas.getContext && canvas.getContext('2d') ) ) {
// ... redirection ...
}
</script>
<script src="js/jquery.js"></script>
<script>
$(function () {
//...
}
</script>
With either of these approaches, the order of execution is:
The first <script>.
The loading of jquery.js, whether done with document.write() or a simple <script> tag.
The final script.
When you insert a script tag like you are, it will be loaded in the background, not immediately and thus your next script will run before jQuery is loaded. You will need to attach a listener such that you know when jQuery is successfully loaded and you can then run your scripts that use jQuery.
Here's an article that describes how to know when a dynamically loaded script is loaded: http://software.intel.com/en-us/blogs/2010/05/22/dynamically-load-javascript-with-load-completion-notification.
FYI, in your specific case, you also could just have a static script tag that loads jQuery, but place your script that detects whether to redirect or not BEFORE the jQuery script tag. That would be the simplest option.
<script type="text/javascript">
var canvas = document.createElement('canvas');
if (!canvas || !canvas.getContext || !canvas.getContext('2d')) {
// redirect here or whatever
}
</script>
<script type="text/javascript" src="jquery.js">
</script>
<script type="text/javascript">
$(function () {
//...
}
</script>
finally working like a charm, I'm relieved myself !
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
<script type="text/javascript">
window.onload = function(){
var jqu = "$(console.log('worked'));";
var canvas = document.createElement('canvas');
if (canvas && canvas.getContext && canvas.getContext('2d')) {
var s = document.getElementsByTagName('head')[0];
var jq = document.createElement('script');
jq.setAttribute('type','text/javascript');
jq.innerHTML = jqu;
var jqLoad = document.createElement('script');
jqLoad.setAttribute('type','text/javascript');
jqLoad.setAttribute('src','jquery-1.10.0.js');
jqLoad.setAttribute('id','jqloader');
s.appendChild(jqLoad);
document.getElementById('jqloader').onload = function(){
console.log('loaded');
s.appendChild(jq);
}
}
else {
// ... redirection ...
}
console.log(document);
}
</script>
</head>
<body>
</body>
</html>
jsbin Demo
explanation :
1- using dom functions to append or insert elements are always the best (dynamic and safer more than anything else), and document.write is not recommended over that.
2- at parse-time, whatever functions you have in your script will be evaluated thus you will get an error if you have the script and not loaded the library yet.
3- loading the library and executing the relevant script in the same tag is not recommended. better do the script in another tag (after loading is done completely) to ensure it will work.
4- events for document.onload ensures that the document is loaded and the doms exist so you can append children to them. as for the document.getElementById('jqloader').onload it was just to insure that the jquery library is loaded completely and added to the document, and only then the script will be added after and evaluated.
As others have said, the reason you're getting an error is because you've loaded jQuery asynchronously and it hasn't loaded yet.
There are two ways to accomplish what you want.
You can poll for window.jQuery, or you can use an asynchronous loader callback.
Since you only load jQuery only when you detect canvas support, you won't have to worry about supporting old browsers.
var async_script_load = function (s, callback) {
var script;
script = document.createElement("script");
script.async = "async";
if (s.scriptCharset) {
script.charset = s.scriptCharset;
}
script.src = s.url;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function () {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
// Remove the script
if (head && script.parentNode) {
head.removeChild(script);
}
// Dereference the script
script = undefined;
callback(200, "success");
}
};
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709 and #4378).
head.insertBefore(script, head.firstChild);
};
async_loader({url:'http://tempuri.org/jquery.min.js'},function() {
//call jquery here.
});
For a polling method, it's as simple as:
var checkJq = function() {
if(window.jQuery) {
//do jQuery
} else {
setTimeout(checkJq,100);
}
}
setTimeout(checkJq,100);

Load external JavaScript files where A requires B being loaded asynchronously

I have a use case where I have a JS file that is lazy loaded. During development this JS file would be a combination of File A and File B. However in production it should load File A then File B for separation.
This works properly when adding them directly to the DOM with
for(int i =0; i < urls.length; i++) {
document.write("<script type='text/javascript' src='" + urls[i] + "'></script>");
}
Even if FileB loads before FileA, it doesn't get evaluated first.
My code is as follows
createScript: function(url, id) {
var m = this;
// flag as loaded
var myonload = function() {
Logger.debug(formatMessage(Library.RESOURCES["load.success"], [this.src, id]), "core");
this.library_loaded = true;
};
// log loading error
var myonerror = function() {
Logger.warn(formatMessage(Library.RESOURCES["load.failure"], [this.src, id]), "core");
};
//If currently being grabbed
// || m.currentAsyc[ids[i]+"/async"]
if (Library.findScript(url))
return;
var script = document.createElement("script");
script.type = "text/javascript";
script.library_loaded = false;
// mozilla
script.onload = myonload;
script.onerror = myonerror;
// ie
script.onreadystatechange = function() {
if (this.readyState == "complete" || this.readyState == "loaded") {
setTimeout(myonload.bindAsEventListener(this), 10);
this.onreadystatechange = null;
}
};
script.src = url;
m.head.appendChild(script);
},
This results in the DOM having
<script type="text/javascript" src="/path/to/FileA"></script>
<script type="text/javascript" src="/path/to/FileB"></script>
But when FileB loads faster then FileA it tries to evaluate itself first.
Is there any way to prevent this?
Further explanation
I am trying to implement a Sprockets like functionality into a legacy system.
When I would normally ask for '/dyn/path/to/FileA' it would have
//= require FileB at the top sending a respones of
document.write("<script type="text/javascript" src="/path/to/FileA"></script>");
document.write("<script type="text/javascript" src="/path/to/FileB"></script>");
This works great as it does the calls synchronously.
In this situation, I have made an AJAX call to see what files the dynamic FileA requires (those would be FileA and FileB). This returns saying that we need to request FileA and FileB. The problem is I need a call back to confirm when both of those files are actually loaded as I have a wait function for other areas that need this code as well. They are currently waiting just for the /dyn/path/to/FileA to complete loading, however we now need them to wait for FileA and FileB to complete their loading as well.

Verify External Script Is Loaded

I'm creating a jquery plugin and I want to verify an external script is loaded. This is for an internal web app and I can keep the script name/location consistent(mysscript.js). This is also an ajaxy plugin that can be called on many times on the page.
If I can verify the script is not loaded I'll load it using:
jQuery.getScript()
How can I verify the script is loaded because I don't want the same script loaded on the page more than once? Is this something that I shouldn't need to worry about due to caching of the script?
Update:
I may not have control over who uses this plugin in our organization and may not be able to enforce that the script is not already on the page with or without a specific ID, but the script name will always be in the same place with the same name. I'm hoping I can use the name of the script to verify it's actually loaded.
If the script creates any variables or functions in the global space you can check for their existance:
External JS (in global scope) --
var myCustomFlag = true;
And to check if this has run:
if (typeof window.myCustomFlag == 'undefined') {
//the flag was not found, so the code has not run
$.getScript('<external JS>');
}
Update
You can check for the existence of the <script> tag in question by selecting all of the <script> elements and checking their src attributes:
//get the number of `<script>` elements that have the correct `src` attribute
var len = $('script').filter(function () {
return ($(this).attr('src') == '<external JS>');
}).length;
//if there are no scripts that match, the load it
if (len === 0) {
$.getScript('<external JS>');
}
Or you can just bake this .filter() functionality right into the selector:
var len = $('script[src="<external JS>"]').length;
Few too many answers on this one, but I feel it's worth adding this solution. It combines a few different answers.
Key points for me were
add an #id tag, so it's easy to find, and not duplicate
Use .onload() to wait until the script has finished loading before using it
mounted() {
// First check if the script already exists on the dom
// by searching for an id
let id = 'googleMaps'
if(document.getElementById(id) === null) {
let script = document.createElement('script')
script.setAttribute('src', 'https://maps.googleapis.com/maps/api/js?key=' + apiKey)
script.setAttribute('id', id)
document.body.appendChild(script)
// now wait for it to load...
script.onload = () => {
// script has loaded, you can now use it safely
alert('thank me later')
// ... do something with the newly loaded script
}
}
}
#jasper's answer is totally correct but with modern browsers, a standard Javascript solution could be:
function isScriptLoaded(src)
{
return Boolean(document.querySelector('script[src="' + src + '"]'));
}
UPDATE July 2021:
The accepted solutions above have changed & improved much over time. The scope of my previous answer above was only to detect if the script was inserted in the document to load (and not whether the script has actually finished loading).
To detect if the script has already loaded, I use the following method (in general):
Create a common library function to dynamically load all scripts.
Before loading, it uses the isScriptLoaded(src) function above to check whether the script has already been added (say, by another module).
I use something like the following loadScript() function to load the script that uses callback functions to inform the calling modules if the script finished loading successfully.
I also use additional logic to retry when script loading fails (in case of temporary network issues).
Retry is done by removing the <script> tag from the body and adding it again.
If it still fails to load after configured number of retries, the <script> tag is removed from the body.
I have removed that logic from the following code for simplicity. It should be easy to add.
/**
* Mark/store the script as fully loaded in a global variable.
* #param src URL of the script
*/
function markScriptFullyLoaded(src) {
window.scriptLoadMap[src] = true;
}
/**
* Returns true if the script has been added to the page
* #param src URL of the script
*/
function isScriptAdded(src) {
return Boolean(document.querySelector('script[src="' + src + '"]'));
}
/**
* Returns true if the script has been fully loaded
* #param src URL of the script
*/
function isScriptFullyLoaded(src) {
return src in window.scriptLoadMap && window.scriptLoadMap[src];
}
/**
* Load a script.
* #param src URL of the script
* #param onLoadCallback Callback function when the script is fully loaded
* #param onLoadErrorCallback Callback function when the script fails to load
* #param retryCount How many times retry laoding the script? (Not implimented here. Logic goes into js.onerror function)
*/
function loadScript(src, onLoadCallback, onLoadErrorCallback, retryCount) {
if (!src) return;
// Check if the script is already loaded
if ( isScriptAdded(src) )
{
// If script already loaded successfully, trigger the callback function
if (isScriptFullyLoaded(src)) onLoadCallback();
console.warn("Script already loaded. Skipping: ", src);
return;
}
// Loading the script...
const js = document.createElement('script');
js.setAttribute("async", "");
js.src = src;
js.onload = () => {
markScriptFullyLoaded(src)
// Optional callback on script load
if (onLoadCallback) onLoadCallback();
};
js.onerror = () => {
// Remove the script node (to be able to try again later)
const js2 = document.querySelector('script[src="' + src +'"]');
js2.parentNode.removeChild(js2);
// Optional callback on script load failure
if (onLoadErrorCallback) onLoadErrorCallback();
};
document.head.appendChild(js);
}
This was very simple now that I realize how to do it, thanks to all the answers for leading me to the solution. I had to abandon $.getScript() in order to specify the source of the script...sometimes doing things manually is best.
Solution
//great suggestion #Jasper
var len = $('script[src*="Javascript/MyScript.js"]').length;
if (len === 0) {
alert('script not loaded');
loadScript('Javascript/MyScript.js');
if ($('script[src*="Javascript/MyScript.js"]').length === 0) {
alert('still not loaded');
}
else {
alert('loaded now');
}
}
else {
alert('script loaded');
}
function loadScript(scriptLocationAndName) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = scriptLocationAndName;
head.appendChild(script);
}
Create the script tag with a specific ID and then check if that ID exists?
Alternatively, loop through script tags checking for the script 'src' and make sure those are not already loaded with the same value as the one you want to avoid ?
Edit: following feedback that a code example would be useful:
(function(){
var desiredSource = 'https://sitename.com/js/script.js';
var scripts = document.getElementsByTagName('script');
var alreadyLoaded = false;
if(scripts.length){
for(var scriptIndex in scripts) {
if(!alreadyLoaded && desiredSource === scripts[scriptIndex].src) {
alreadyLoaded = true;
}
}
}
if(!alreadyLoaded){
// Run your code in this block?
}
})();
As mentioned in the comments (https://stackoverflow.com/users/1358777/alwin-kesler), this may be an alternative (not benchmarked):
(function(){
var desiredSource = 'https://sitename.com/js/script.js';
var scripts = document.getElementsByTagName('script');
var alreadyLoaded = false;
for(var scriptIndex in document.scripts) {
if(!alreadyLoaded && desiredSource === scripts[scriptIndex].src) {
alreadyLoaded = true;
}
}
if(!alreadyLoaded){
// Run your code in this block?
}
})();
Simply check if the global variable is available, if not check again. In order to prevent the maximum callstack being exceeded set a 100ms timeout on the check:
function check_script_loaded(glob_var) {
if(typeof(glob_var) !== 'undefined') {
// do your thing
} else {
setTimeout(function() {
check_script_loaded(glob_var)
}, 100)
}
}
Another way to check an external script is loaded or not, you can use data function of jquery and store a validation flag. Example as :
if(!$("body").data("google-map"))
{
console.log("no js");
$.getScript("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=initilize",function(){
$("body").data("google-map",true);
},function(){
alert("error while loading script");
});
}
}
else
{
console.log("js already loaded");
}
I think it's better to use window.addEventListener('error') to capture the script load error and try to load it again.
It's useful when we load scripts from a CDN server. If we can't load script from the CDN, we can load it from our server.
window.addEventListener('error', function(e) {
if (e.target.nodeName === 'SCRIPT') {
var scriptTag = document.createElement('script');
scriptTag.src = e.target.src.replace('https://static.cdn.com/', '/our-server/static/');
document.head.appendChild(scriptTag);
}
}, true);
Merging several answers from above into an easy to use function
function GetScriptIfNotLoaded(scriptLocationAndName)
{
var len = $('script[src*="' + scriptLocationAndName +'"]').length;
//script already loaded!
if (len > 0)
return;
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = scriptLocationAndName;
head.appendChild(script);
}
My idead is to listen the error log if there is an error on script loading.
const checkSegmentBlocked = (e) => {
if (e.target.nodeName === 'SCRIPT' && e.target.src.includes('analytics.min.js')) {
window.isSegmentBlocked = true;
e.target.removeEventListener(e.type, checkSegmentBlocked);
}
};
window.addEventListener('error', checkSegmentBlocked, true);
Some answers on this page are wrong. They check for the existence of the <script> tag - but that is not enough. That tells you that the tag was inserted into the DOM, not that the script is finished loading.
I assume from the question that there are two parts: the code that inserts the script, and the code that checks whether the script has loaded.
The code that dynamically inserts the script:
let tag = document.createElement('script');
tag.type = 'text/javascript';
tag.id = 'foo';
tag.src = 'https://cdn.example.com/foo.min.js';
tag.onload = () => tag.setAttribute('data-loaded', true); // magic sauce
document.body.appendChild(tag);
Some other code, that checks whether the script has loaded:
let script = document.getElementById('foo');
let isLoaded = script && script.getAttribute('data-loaded') === 'true';
console.log(isLoaded); // true
If the both of those things (inserting and checking) are in the same code block, then you could simplify the above:
tag.onload = () => console.log('loaded');
I found a quick tip before you start diving into code that might save a bit of time. Check devtools on the webpage and click on the network tab. The js scripts are shown if they are loaded as a 200 response from the server.

JavaScript to detect when external javascripts are loading

Is there a way (event listener or otherwise) to detect when a particular external javascript is about to load / is loading / has finished loading?
In otherwords, does the browser fire an event when it's about to load, is loading, and/or has finished loading a particular external script?
For my purposes it's not enough to simply check to see if a known object exists or anything like that. Instead, I need something that will detect a JS file is loading/loaded regardless of the contents of the JS file.
The following example works in Chrome. It attaches an handler on the onload event of the head tag and then adds an external javascript file. When the file is loaded, the event is captured and an alert appears.
http://jsfiddle.net/francisfortier/uv9Fh/
window.onload = function() {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", "http://code.jquery.com/jquery-1.7.1.min.js");
head.addEventListener("load", function(event) {
if (event.target.nodeName === "SCRIPT")
{
alert("Script loaded: " + event.target.getAttribute("src"));
}
}, true);
head.appendChild(script);
}
Since all browsers blocks the "UI thread" when processing <script> tags, you can rely that pre-existing tags are loaded.
If you are loading a script dynamically, you can listen to the load event of the <script> tag.
function loadScript(src, callback) {
var script = document.createElement("script");
script.setAttribute("src", src);
script.addEventListener("load", callback);
document.getElementsByTagName("script")[0].insertBefore(script);
};
loadScript("http://code.jquery.com/jquery-1.7.1.min.js", function(){
alert("loading is done");
});
<script onload> will fire when a script is finished loading.
You will not be able to do something like:
<script src="/foo.js"></script>
<script src="/bar.js"></script>
<script>
function alertonload(src){
console.log(src+' is loaded');
}
scripts = document.getElementsByTagName('script');
for(var i=0; i<scripts.length; i++){
scripts[i].onload = function(){ alertonload(scripts[i].src); };
}
</script>
This is pure conjecture and speculation; I have not tried it and there's probably better ways to write it, but this will not do what you're looking to do. EDIT: The scripts are loaded as the browser sees them, not after the fact. They will be loaded before this occurs.
I wrote a script for those who wants to call a function after all external files (dynamically added) are loaded. It goes like this:
var file = [];
var loaded = [];
var head = document.getElementsByTagName('head')[0];
var fileOnLoad =
// Pass the arrays to your function
(function(file, loaded){ return function(){
loaded.push(true);
// Print the number of files loaded
document.getElementById("demo").innerHTML +=
"<br>"+loaded.length+" files loaded";
if(file.length == loaded.length){
alert("All files are loaded!");
}
}})(file, loaded);
////////////////////////////////////////////////
//// ////
//// Add the external files dynamically ////
//// ////
////////////////////////////////////////////////
file[0] = document.createElement('script');
file[0].src =
"https://maps.googleapis.com/maps/api/js?v=3.exp";
file[0].onload = fileOnLoad;
head.appendChild(file[0]);
file[1] = document.createElement('script');
file[1].src =
"http://code.jquery.com/jquery-latest.js";
file[1].onload = fileOnLoad;
head.appendChild(file[1]);
file[2] = document.createElement('script');
file[2].src =
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js";
file[2].onload = fileOnLoad;
head.appendChild(file[2]);
file[3] = document.createElement('link');
file[3].rel = "stylesheet";
file[3].href =
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css";
file[3].onload = fileOnLoad;
head.appendChild(file[3]);
file[4] = document.createElement('link');
file[4].rel = "stylesheet";
file[4].href =
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css";
file[4].onload = fileOnLoad;
head.appendChild(file[4]);
<div id="demo">0 file loaded</div>
Hope it helps!
If you can use jQuery, try $.getScript(). Docs here.

Categories