1

I don’t want to delay rendering of my page if an external resource takes a while to load and so I implemented this logic …

<script type="text/javascript">
    function importScript (sSrc, fOnload) {
        var oScript = document.createElement("script");
        oScript.type = "text\/javascript";
        oScript.defer = true;
        if (fOnload) { oScript.onload = fOnload; }
        document.currentScript.parentNode.insertBefore(oScript, document.currentScript);
        oScript.src = sSrc;
    }

    importScript(“//thirdpartysite.com/theirscript.js", function () { doStuff(); });
     });
</script>

This works great except on Mac Firefox (I’m using version 45.0.1). ON that browser, the page does not render until this resource has been loaded. Does anyone know a way to defer loading of the resource without blocking rendering of the page that preferably works on both Chrome and Firefox (all browsers would be nice, but I don’t have time to test them all).

Dave
  • 15,639
  • 133
  • 442
  • 830
  • 1
    not tested, just few ideas: `oScript.async = true;` then `document.body.appendChild(oScript);` instead of adding it to the header (most likely) and if this doesn't work you could try to delay the execution of the code inside the function, till after `DOMContentLoaded`. – Thomas Apr 07 '16 at 23:14
  • Am I removing any of the lines above to replace what you have here? – Dave Apr 14 '16 at 14:05
  • Maybe this answer can help you http://stackoverflow.com/questions/7718935/load-scripts-asynchronously – Agu V Apr 15 '16 at 02:02
  • move all the – Jacob Nelson Apr 19 '16 at 06:43

1 Answers1

1

Since you already have a system that takes an URL and then calls a callback, you can easily insert a setTimeout there. Something like

function importScript(sSrc, fOnload) {
    setTimeout(function () {
        var oScript = document.createElement("script");
        oScript.type = "text\/javascript";
        oScript.defer = true;
        if (fOnload) {
            oScript.onload = fOnload;
        }
        document.currentScript.parentNode.insertBefore(oScript, document.currentScript);
        oScript.src = sSrc;
    }, 100);
}

And of course, execute the imports on the DOMReady event. This will pretty much hack any rendering blocking that may occur.

However the system becomes exceedingly complex when you add dependencies, code that needs to be executed only if the component has loaded completely. That leads me to believe that a more fluent interface (like a Promise system) could improve your design and also maybe trickle the timeout and even the deferred execution until DOMReady has been fired to all the elements involved.

Here is an example without a promise like syntax, but assuming all the third party scripts and dependencies start from one point only:

var loadingChainStarted = false;
function importScript(sSrc, fOnload) {
    if (document.readyState !== "complete") {
        window.addEventListener("onload", function () {
            importScript(sSrc, fOnload);
        }, false);
        return;
    }
    if (!loadingChainStarted) {
        loadingChainStarted = true;
        setTimeout(function () {
            importScript(sSrc, fOnload);
        }, 100);
        return;
    }
    var oScript = document.createElement("script");
    oScript.type = "text\/javascript";
    oScript.defer = true;
    if (fOnload) {
        oScript.onload = fOnload;
    }
    document.currentScript.parentNode.insertBefore(oScript, document.currentScript);
    oScript.src = sSrc;
}

This will only execute the script after DOMReady and, for the first time, after a 100 millisecond delay. The rest of the chain would be executed normally as both DOMReady has been fired and the delay has elapsed.

Siderite Zackwehdex
  • 6,293
  • 3
  • 30
  • 46