1

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();
        }
    }
}
Community
  • 1
  • 1
Božo Stojković
  • 2,893
  • 1
  • 27
  • 52

1 Answers1

5

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.

Community
  • 1
  • 1
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • Oh, I didn't know that. I'm stupid for not figuring that out already. Thanks! – Božo Stojković Nov 17 '16 at 19:24
  • Then I will just do something like adding `window.script_loaded = true;` to start of each script and check it. If it's true, set it to false and call the `success` callback. Else call the `error` callback. – Božo Stojković Nov 17 '16 at 19:26
  • @Slayther Let me insist it's an inconsistence. `injectJS("parse error", true, _=>console.log("Success!"), _=>console.log("Error!"));` will log error but `injectJS("data:text/javascript,parse error", false, _=>console.log("Success!"), _=>console.log("Error!"));` will log success. The `load` and `error` events are not supposed to validate the script, only inform if it can be fetched. – Oriol Nov 17 '16 at 19:57
  • I actually consider the second case as success as it's only important for me to know whether the script loaded or not. If it's an external script, then errors do not play a role, but only HTTP or other, for example browser-specific, errors outside of the script itself. – Božo Stojković Nov 17 '16 at 20:02
  • You can see this in my question. The second case is URL of perfectly working JS. As Chrome spews out an error: `Refused to execute script from 'https://raw.githubusercontent.com/eligrey/FileSaver.js/master/FileSaver.js' because its MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.`, the script isn't loaded and it has to call the `error` callback. – Božo Stojković Nov 17 '16 at 20:18