Joe
Joe

Reputation: 1772

javascript - how to dynamically embed a blocking script?

I have a javascript tag embedded in the <head> of a webpage. In this script (served via Amazon Cloudfront) it does some processing and then if certain checks are set, appends another script to the document head. Here is an example of that embed:

    var js = document.createElement('script');
    js.src = 'http://cdn.mysite.com/script.js?cache_bust='+Math.random();
    document.getElementsByTagName('head')[0].appendChild(js);

This works properly, however it's non-blocking meaning the webpage continues to load while that script is being appended to the head.

Is there any way to embed a script in a blocking manner so the webpage "hangs" while the new script is setup?

I was thinking about maybe moving the first script to just below the <body> tag and then using document.write() the second script but I'd prefer to keep it in the <head>.

Any ideas? Thanks!

Upvotes: 1

Views: 275

Answers (2)

bevacqua
bevacqua

Reputation: 48516

Use something like this script loader I made:

    var scriptLoader = [];

    /*
    * loads a script and defers a callback for when the script finishes loading.
    * you can also just stack callbacks on the script load by invoking this method repeatedly.
    *
    * opts format:  {
    *       url: the url of the target script resource,
    *       timeout: a timeout in milliseconds after which any callbacks on the script will be dropped, and the script element removed.
    *       callbacks: an optional array of callbacks to execute after the script completes loading.
    *       callback: an optional callback to execute after the script completes loading.
    *       before: an optional callback to execute before the script is loaded, only intended to be ran prior to requesting the script, not multiple times.
    *       success: an optional callback to execute when the script successfully loads, always remember to call script.complete at the end.
    *       error: an optional callback to execute when and if the script request fails.
    *   }
    */
    function loadScript(opts) {
        if (typeof opts === "string") {
            opts = {
                url: opts
            };
        }
        var script = scriptLoader[opts.url];
        if (script === void 0) {
            var complete = function (s) {
                s.status = "loaded";
                s.executeCallbacks();
            };

            script = scriptLoader[opts.url] = {
                url: opts.url,
                status: "loading",
                requested: new Date(),
                timeout: opts.timeout || 10000,
                callbacks: opts.callbacks || [opts.callback || $.noop],
                addCallback: function (callback) {
                    if (!!callback) {
                        if (script.status !== "loaded") {
                            script.callbacks.push(callback);
                        } else {
                            callback();
                        }
                    }
                },
                executeCallbacks: function () {
                    $.each(script.callbacks, function () {
                        this();
                    });
                    script.callbacks = [];
                },
                before: opts.before || $.noop,
                success: opts.success || complete,
                complete: complete,
                error: opts.error || $.noop
            };

            script.before();

            $.ajax(script.url, {
                timeout: script.timeout,
                success: function () {
                    script.success(script);
                },
                error: function () {
                    script.error(); // .error should remove anything added by .before
                    scriptLoader[script.url] = void 0; // dereference, no callbacks were executed, no harm is done.
                }
            });
        } else {
            script.addCallback(opts.callback);
        }
    }

    loadScript({
        url: 'http://fiddle.jshell.net/js/lib/mootools-core-1.4.5-nocompat.js',
        callback: function(){
            alert('foo');
        }
    });

Generally speaking, you should be deferring execution, rather than blocking, giving your user an increased perceived page loading speed.

Upvotes: 1

bfavaretto
bfavaretto

Reputation: 71939

You're right, document.write should work. As Dr.Molle mentioned in the comment, you can use inside the <head> too (the <head> is part of the document too!).

So:

<head>
    <script>
    var src = 'http://cdn.mysite.com/script.js?cache_bust='+Math.random();
    document.write '<script src="">'+ src + '</scr' + 'ipt>';
    </script>
</head>

The '</scr' + 'ipt>' part is a precaution, browsers usually think the outer script block is being closed when they find </script>, even if inside a string.

Upvotes: 1

Related Questions