How to tell if a <script> tag failed to load

JavascriptOnerrorScript Tag

Javascript Problem Overview


I'm dynamically adding <script> tags to a page's <head>, and I'd like to be able to tell whether the loading failed in some way -- a 404, a script error in the loaded script, whatever.

In Firefox, this works:

var script_tag = document.createElement('script');
script_tag.setAttribute('type', 'text/javascript');
script_tag.setAttribute('src', 'http://fail.org/nonexistant.js');
script_tag.onerror = function() { alert("Loading failed!"); }
document.getElementsByTagName('head')[0].appendChild(script_tag);

However, this doesn't work in IE or Safari.

Does anyone know of a way to make this work in browsers other than Firefox?

(I don't think a solution that requires placing special code within the .js files is a good one. It's inelegant and inflexible.)

Javascript Solutions


Solution 1 - Javascript

There is no error event for the script tag. You can tell when it is successful, and assume that it has not loaded after a timeout:

<script type="text/javascript" onload="loaded=1" src="....js"></script>

Solution 2 - Javascript

UPDATE 2021: All browsers today support onerror="" on script tags, examples:

Original comment from 2010:

If you only care about html5 browsers you can use error event.

From the spec: > If the src attribute's value is the > empty string or if it could not be > resolved, then the user agent must > queue a task to fire a simple event > named error at the element, and > abort these steps.

(...)

> If the load resulted in an error (for > example a DNS error, or an HTTP 404 > error) Executing the script block must > just consist of firing a simple event > named error at the element.

This means you don't have to do any error prone polling and can combine it with async and defer attribute to make sure the script is not blocking page rendering: > The defer attribute may be specified > even if the async attribute is > specified, to cause legacy Web > browsers that only support defer (and > not async) to fall back to the defer > behavior instead of the synchronous > blocking behavior that is the default.

More on http://www.w3.org/TR/html5/scripting-1.html#script

Solution 3 - Javascript

my working clean solution (2017)

function loaderScript(scriptUrl){
   return new Promise(function (res, rej) {
    let script = document.createElement('script');
    script.src = scriptUrl;
    script.type = 'text/javascript';
    script.onError = rej;
    script.async = true;
    script.onload = res;
    script.addEventListener('error',rej);
    script.addEventListener('load',res);
    document.head.appendChild(script);
 })

}

As Martin pointed, used like that:

const event = loaderScript("myscript.js")
  .then(() => { console.log("loaded"); })
  .catch(() => { console.log("error"); });

OR

try{
 await loaderScript("myscript.js")
 console.log("loaded"); 
}catch{
 console.log("error");
}

Solution 4 - Javascript

The script from Erwinus works great, but isn't very clearly coded. I took the liberty to clean it up and decipher what it was doing. I've made these changes:

  • Meaningful variable names
  • Use of prototype.
  • require() uses an argument variable
  • No alert() messages are returned by default
  • Fixed some syntax errors and scope issues I was getting

Thanks again to Erwinus, the functionality itself is spot on.

function ScriptLoader() {
}

ScriptLoader.prototype = {

    timer: function (times, // number of times to try
                     delay, // delay per try
                     delayMore, // extra delay per try (additional to delay)
                     test, // called each try, timer stops if this returns true
                     failure, // called on failure
                     result // used internally, shouldn't be passed
            ) {
        var me = this;
        if (times == -1 || times > 0) {
            setTimeout(function () {
                result = (test()) ? 1 : 0;
                me.timer((result) ? 0 : (times > 0) ? --times : times, delay + ((delayMore) ? delayMore : 0), delayMore, test, failure, result);
            }, (result || delay < 0) ? 0.1 : delay);
        } else if (typeof failure == 'function') {
            setTimeout(failure, 1);
        }
    },

    addEvent: function (el, eventName, eventFunc) {
        if (typeof el != 'object') {
            return false;
        }

        if (el.addEventListener) {
            el.addEventListener(eventName, eventFunc, false);
            return true;
        }

        if (el.attachEvent) {
            el.attachEvent("on" + eventName, eventFunc);
            return true;
        }

        return false;
    },

    // add script to dom
    require: function (url, args) {
        var me = this;
        args = args || {};

        var scriptTag = document.createElement('script');
        var headTag = document.getElementsByTagName('head')[0];
        if (!headTag) {
            return false;
        }

        setTimeout(function () {
            var f = (typeof args.success == 'function') ? args.success : function () {
            };
            args.failure = (typeof args.failure == 'function') ? args.failure : function () {
            };
            var fail = function () {
                if (!scriptTag.__es) {
                    scriptTag.__es = true;
                    scriptTag.id = 'failed';
                    args.failure(scriptTag);
                }
            };
            scriptTag.onload = function () {
                scriptTag.id = 'loaded';
                f(scriptTag);
            };
            scriptTag.type = 'text/javascript';
            scriptTag.async = (typeof args.async == 'boolean') ? args.async : false;
            scriptTag.charset = 'utf-8';
            me.__es = false;
            me.addEvent(scriptTag, 'error', fail); // when supported
            // when error event is not supported fall back to timer
            me.timer(15, 1000, 0, function () {
                return (scriptTag.id == 'loaded');
            }, function () {
                if (scriptTag.id != 'loaded') {
                    fail();
                }
            });
            scriptTag.src = url;
            setTimeout(function () {
                try {
                    headTag.appendChild(scriptTag);
                } catch (e) {
                    fail();
                }
            }, 1);
        }, (typeof args.delay == 'number') ? args.delay : 1);
        return true;
    }
};

$(document).ready(function () {
    var loader = new ScriptLoader();
    loader.require('resources/templates.js', {
        async: true, success: function () {
            alert('loaded');
        }, failure: function () {
            alert('NOT loaded');
        }
    });
});

Solution 5 - Javascript

I know this is an old thread but I got a nice solution to you (I think). It's copied from an class of mine, that handles all AJAX stuff.

When the script cannot be loaded, it set an error handler but when the error handler is not supported, it falls back to a timer that checks for errors for 15 seconds.

function jsLoader()
{
    var o = this;

    // simple unstopable repeat timer, when t=-1 means endless, when function f() returns true it can be stopped
    o.timer = function(t, i, d, f, fend, b)
    {
        if( t == -1 || t > 0 )
        {
            setTimeout(function() {
                b=(f()) ? 1 : 0;
                o.timer((b) ? 0 : (t>0) ? --t : t, i+((d) ? d : 0), d, f, fend,b );
            }, (b || i < 0) ? 0.1 : i);
        }
        else if(typeof fend == 'function')
        {
            setTimeout(fend, 1);
        }
    };
    
    o.addEvent = function(el, eventName, eventFunc)
    {
        if(typeof el != 'object')
        {
            return false;
        }

        if(el.addEventListener)
        {
            el.addEventListener (eventName, eventFunc, false);
            return true;
        }

        if(el.attachEvent)
        {
            el.attachEvent("on" + eventName, eventFunc);
            return true;
        }

        return false;
    };
    
    // add script to dom
    o.require = function(s, delay, baSync, fCallback, fErr)
    {
        var oo = document.createElement('script'),
        oHead = document.getElementsByTagName('head')[0];
        if(!oHead)
        {
            return false;
        }
    
        setTimeout( function() {
            var f = (typeof fCallback == 'function') ? fCallback : function(){};
            fErr = (typeof fErr == 'function') ? fErr : function(){
                alert('require: Cannot load resource -'+s);
            },
            fe = function(){
                if(!oo.__es)
                {
                    oo.__es = true;
                    oo.id = 'failed';
                    fErr(oo);
                }
            };
            oo.onload = function() {
                oo.id = 'loaded';
                f(oo);
            };
            oo.type = 'text/javascript';
            oo.async = (typeof baSync == 'boolean') ? baSync : false;
            oo.charset = 'utf-8';
            o.__es = false;
            o.addEvent( oo, 'error', fe ); // when supported
            
            // when error event is not supported fall back to timer
            o.timer(15, 1000, 0, function() {
                return (oo.id == 'loaded');
            }, function(){ 
                if(oo.id != 'loaded'){
                    fe();
                }
            });
            oo.src = s;
            setTimeout(function() {
                try{
                    oHead.appendChild(oo);
                }catch(e){
                    fe();
                }
            },1); 
        }, (typeof delay == 'number') ? delay : 1);  
        return true;
    };
    
}
    
$(document).ready( function()
{
    var ol = new jsLoader();
    ol.require('myscript.js', 800, true, function(){
        alert('loaded');
    }, function() {
        alert('NOT loaded');
    });
});

Solution 6 - Javascript

To check if the javascript in nonexistant.js returned no error you have to add a variable inside http://fail.org/nonexistant.js like var isExecuted = true; and then check if it exists when the script tag is loaded.

However if you only want to check that the nonexistant.js returned without a 404 (meaning it exists), you can try with a isLoaded variable ...

var isExecuted = false;
var isLoaded = false;
script_tag.onload = script_tag.onreadystatechange = function() {
    if(!this.readyState ||
        this.readyState == "loaded" || this.readyState == "complete") {
        // script successfully loaded
        isLoaded = true;
        
        if(isExecuted) // no error
    }
}

This will cover both cases.

Solution 7 - Javascript

I hope this doesn't get downvoted, because in special circumstances it is the most reliable way to solve the problem. Any time the server allows you to get a Javascript resource using CORS (http://en.wikipedia.org/wiki/Cross-origin_resource_sharing), you have a rich array of options to do so.

Using XMLHttpRequest to fetch resources will work across all modern browsers, including IE. Since you are looking to load Javascript, you have Javascript available to you in the first place. You can track the progress using the readyState (http://en.wikipedia.org/wiki/XMLHttpRequest#The_onreadystatechange_event_listener). Finally, once you receive the content of the file, you can execute it with eval ( ). Yes, I said eval -- because security-wise it is no different from loading the script normally. In fact, a similar technique is suggested by John Resig to have nicer

Solution 8 - Javascript

This trick worked for me, although I admit that this is probably not the best way to solve this problem. Instead of trying this, you should see why the javascripts aren't loading. Try keeping a local copy of the script in your server, etc. or check with the third party vendor from where you are trying to download the script.

Anyways, so here's the workaround:

  1. Initialize a variable to false

  2. Set it to true when the javascript loads (using the onload attribute)

  3. check if the variable is true or false once the HTML body has loaded

Solution 9 - Javascript

Here is another JQuery-based solution without any timers:

<script type="text/javascript">
function loadScript(url, onsuccess, onerror) {
$.get(url)
	.done(function() {
		// File/url exists
		console.log("JS Loader: file exists, executing $.getScript "+url)
		$.getScript(url, function() {
			if (onsuccess) {
				console.log("JS Loader: Ok, loaded. Calling onsuccess() for " + url);
				onsuccess();
				console.log("JS Loader: done with onsuccess() for " + url);
			} else {
				console.log("JS Loader: Ok, loaded, no onsuccess() callback " + url)
			}
		});
    }).fail(function() {
   			// File/url does not exist
			if (onerror) {
				console.error("JS Loader: probably 404 not found. Not calling $.getScript. Calling onerror() for " + url);
	       		onerror();
				console.error("JS Loader: done with onerror() for " + url);
			} else {
				console.error("JS Loader: probably 404 not found. Not calling $.getScript. No onerror() callback " + url);
			}
 	});
}
</script>

Thanks to: https://stackoverflow.com/a/14691735/1243926

Sample usage (original sample from JQuery getScript documentation):

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>jQuery.getScript demo</title>
  <style>
  .block {
     background-color: blue;
     width: 150px;
     height: 70px;
     margin: 10px;
  }
  </style>
  <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>
 
<button id="go">&raquo; Run</button>
<div class="block"></div>
 
<script>


function loadScript(url, onsuccess, onerror) {
$.get(url)
	.done(function() {
		// File/url exists
		console.log("JS Loader: file exists, executing $.getScript "+url)
		$.getScript(url, function() {
			if (onsuccess) {
				console.log("JS Loader: Ok, loaded. Calling onsuccess() for " + url);
				onsuccess();
				console.log("JS Loader: done with onsuccess() for " + url);
			} else {
				console.log("JS Loader: Ok, loaded, no onsuccess() callback " + url)
			}
		});
    }).fail(function() {
   			// File/url does not exist
			if (onerror) {
				console.error("JS Loader: probably 404 not found. Not calling $.getScript. Calling onerror() for " + url);
	       		onerror();
				console.error("JS Loader: done with onerror() for " + url);
			} else {
				console.error("JS Loader: probably 404 not found. Not calling $.getScript. No onerror() callback " + url);
			}
 	});
}


loadScript("https://raw.github.com/jquery/jquery-color/master/jquery.color.js", function() {
  console.log("loaded jquery-color");
  $( "#go" ).click(function() {
    $( ".block" )
      .animate({
        backgroundColor: "rgb(255, 180, 180)"
      }, 1000 )
      .delay( 500 )
      .animate({
        backgroundColor: "olive"
      }, 1000 )
      .delay( 500 )
      .animate({
        backgroundColor: "#00f"
      }, 1000 );
  });
}, function() { console.error("Cannot load jquery-color"); });


</script>
</body>
</html>

Solution 10 - Javascript

This can be done safely using promises

    function loadScript(src) {
      return new Promise(function(resolve, reject) {
        let script = document.createElement('script');
        script.src = src;
    
        script.onload = () => resolve(script);
        script.onerror = () => reject(new Error("Script load error: " + src));
    
        document.head.append(script);
      });
    }

and use like this

    let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js");
    
    promise.then(
      script => alert(`${script.src} is loaded!`),
      error => alert(`Error: ${error.message}`)
    );

Solution 11 - Javascript

onerror Event

*Update August 2017: onerror is fired by Chrome and Firefox. onload is fired by Internet Explorer. Edge fires neither onerror nor onload. I wouldnt use this method but it could work in some cases. See also

https://stackoverflow.com/questions/30171270/link-onerror-do-not-work-in-ie

Definition and Usage The onerror event is triggered if an error occurs while loading an external file (e.g. a document or an image).

Tip: When used on audio/video media, related events that occurs when there is some kind of disturbance to the media loading process, are:

  • onabort
  • onemptied
  • onstalled
  • onsuspend

In HTML:

element onerror="myScript">

In JavaScript, using the addEventListener() method:

object.addEventListener("error", myScript);

Note: The addEventListener() method is not supported in Internet Explorer 8 and earlier versions.

Example Execute a JavaScript if an error occurs when loading an image:

img src="image.gif" onerror="myFunction()">

Solution 12 - Javascript

The reason it doesn't work in Safari is because you're using attribute syntax. This will work fine though:

script_tag.addEventListener('error', function(){/*...*/}, true);

...except in IE.

If you want to check the script executed successfully, just set a variable using that script and check for it being set in the outer code.

Solution 13 - Javascript

This doesn't need jquery, doesn't need to load the script async, needs no timer nor to have the loaded script set a value. I've tested it in FF, Chrome, and Safari.

<script>
        function loadScript(src) {
          return new Promise(function(resolve, reject) {

            let s = window.document.createElement("SCRIPT");

            s.onload = () => resolve(s);
            s.onerror = () => reject(new Error(src));
            s.src = src;
            // don't bounce to global handler on 404.
            s.addEventListener('error', function() {});
            window.document.head.append(s);
          }); 
        }   
                        
        let successCallback = (result) => {
          console.log(scriptUrl + " loaded.");
        }   
                        
        let failureCallback = (error) => {
          console.log("load failed: " + error.message);
        }   
                        
        loadScript(scriptUrl).then(successCallback, failureCallback);
</script>

Solution 14 - Javascript

It was proposed to set a timeout and then assume load failure after a timeout.

setTimeout(fireCustomOnerror, 4000);

The problem with that approach is that the assumption is based on chance. After your timeout expires, the request is still pending. The request for the pending script may load, even after the programmer assumed that load won't happen.

If the request could be canceled, then the program could wait for a period, then cancel the request.

Solution 15 - Javascript

Well, the only way I can think of doing everything you want is pretty ugly. First perform an AJAX call to retrieve the Javascript file contents. When this completes you can check the status code to decide if this was successful or not. Then take the responseText from the xhr object and wrap it in a try/catch, dynamically create a script tag, and for IE you can set the text property of the script tag to the JS text, in all other browsers you should be able to append a text node with the contents to script tag. If there's any code that expects a script tag to actually contain the src location of the file, this won't work, but it should be fine for most situations.

Solution 16 - Javascript

This is how I used a promise to detect loading errors that are emited on the window object:

<script type='module'>
window.addEventListener('error', function(error) {
  let url = error.filename
  url = url.substring(0, (url.indexOf("#") == -1) ? url.length : url.indexOf("#"));
  url = url.substring(0, (url.indexOf("?") == -1) ? url.length : url.indexOf("?"));
  url = url.substring(url.lastIndexOf("/") + 1, url.length);
  window.scriptLoadReject && window.scriptLoadReject[url] && window.scriptLoadReject[url](error);
}, true);
window.boot=function boot() {
  const t=document.createElement('script');
  t.id='index.mjs';
  t.type='module';
  new Promise((resolve, reject) => {
    window.scriptLoadReject = window.scriptLoadReject || {};
    window.scriptLoadReject[t.id] = reject;
    t.addEventListener('error', reject);
    t.addEventListener('load', resolve); // Careful load is sometimes called even if errors prevent your script from running! This promise is only meant to catch errors while loading the file.
  }).catch((value) => {
    document.body.innerHTML='Error loading ' + t.id + '! Please reload this webpage.<br/>If this error persists, please try again later.<div><br/>' + t.id + ':' + value.lineno + ':' + value.colno + '<br/>' + (value && value.message);
  });
  t.src='./index.mjs'+'?'+new Date().getTime();
  document.head.appendChild(t);
};
</script>
<script nomodule>document.body.innerHTML='This website needs ES6 Modules!<br/>Please enable ES6 Modules and then reload this webpage.';</script>
</head>

<body onload="boot()" style="margin: 0;border: 0;padding: 0;text-align: center;">
  <noscript>This website needs JavaScript!<br/>Please enable JavaScript and then reload this webpage.</noscript>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavidView Question on Stackoverflow
Solution 1 - JavascriptDiodeus - James MacFarlaneView Answer on Stackoverflow
Solution 2 - JavascriptandreromView Answer on Stackoverflow
Solution 3 - Javascriptpery mimonView Answer on Stackoverflow
Solution 4 - JavascriptAram KocharyanView Answer on Stackoverflow
Solution 5 - JavascriptCodebeatView Answer on Stackoverflow
Solution 6 - JavascriptLuca MatteisView Answer on Stackoverflow
Solution 7 - JavascriptGregory MagarshakView Answer on Stackoverflow
Solution 8 - JavascriptGautam TandonView Answer on Stackoverflow
Solution 9 - JavascriptSergey OrshanskiyView Answer on Stackoverflow
Solution 10 - JavascriptBeingninView Answer on Stackoverflow
Solution 11 - JavascriptWilliam MonahanView Answer on Stackoverflow
Solution 12 - Javascriptuser42092View Answer on Stackoverflow
Solution 13 - JavascriptAlan WendtView Answer on Stackoverflow
Solution 14 - JavascriptGarrettView Answer on Stackoverflow
Solution 15 - JavascriptnickjsifyView Answer on Stackoverflow
Solution 16 - JavascriptMi GView Answer on Stackoverflow