0

It seems like very common practice to load scripts at the end of a page now / not in the <head>. The problem is when you need to run some jQuery inline in the body due to template structure, variable access, etc., depending on project.

What is the easiest way (preferably vanilla JS) to queue a function to run after jQuery becomes defined later?

There are lots of complicated JS libraries for handling this and I'd prefer a brain dead / understandable process with zero documentation.

Is there an accepted "one liner" that's useful without loads of code?

Here's round 1 with CoffeeScript that works, just to show I'm putting some effort into answering it myself. Next up I will convert this to JS and try to come up with a simpler api.

class root.DeferUntil
    constructor: ({@deferredFunction, @deferralCheck} = {}) ->
        @interval = setInterval @executeIfLoaded, 500

    executeIfLoaded: =>
        if not @executed and @deferralCheck()
            @deferredFunction()
            clearInterval @interval

new DeferUntil
     deferredFunction: ->
         console.log "Hi, #{$} shouldn't raise errors"
     deferralCheck: ->
         $ != undefined
jfriend00
  • 683,504
  • 96
  • 985
  • 979
Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245

2 Answers2

2

I've used something like this in the past:

window.Preload = $.extend [],
  run: (e) -> e(window.$)
  load: ->
    @shift = @run
    @push = @run
    @run(func) for func in @
    @splice(0, @length)

Then, you can just push your functions into the Preload array:

Preload.push ($) ->
  # Do something with jQuery here

And after jQuery is loaded:

Preload.load()

That'll execute all your preload functions, and if load() has already been called, functions pushed onto Preload just get executed immediately. This pattern is useful for all kinds of asynchronous load behavior, where you want to run some functions only after a precondition has been met, without having to necessarily know if your precondition is met yet or not.

Chris Heald
  • 61,439
  • 10
  • 123
  • 137
1

If you're controlling the page layout, then it probably goes without saying that the simplest way is to put the function call to your function after jQuery at the end of your page.


If you can put some code after the jQuery script that you're waiting for, but want that code to be generic, not specifically tied to your other code, then you could put something like this.

Put this before your code:

var readyFns = [];
function runReady() {
    for (var i = 0; i < readyFns.length; i++) {
        readyFns[i]();
    }
}

function addReady(fn) {
    readyFns.push(fn);
}

Put this AFTER the jQuery script tag:

$(document).ready(runReady);

The, anywhere in your code (after the first block of code, but before jQuery), you can just schedule any read function with this:

addReady(function() {
    // put your code here
});

If you really want something hands-off, you can poll for when jQuery is loaded.

(function() {
    function check() {
        if (window.jQuery) {
            // put your jQuery code here
        } else {
            setTimeout(check, 1);
        }
    }
    setTimeout(check, 1);

})();

What makes anything else difficult is that what you want to do is to wait for a script tag to load that isn't parsed yet. Because it's not parsed yet, you can't install any event handlers on it.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Can you provide doc on the setTimeout assertion? I've never seen that before. – Chris Heald Dec 30 '13 at 01:23
  • @ChrisHeald - I added another option if you want to go that way, but because parsing/JS execution is single threaded, there's no way for the browser to execute a `setTimeout()` until it returns to its event loop which only happens when the page is done being parsed. – jfriend00 Dec 30 '13 at 01:32
  • http://jsfiddle.net/7DDpR/1/ shows that setTimeout is prone to racing - the first time it runs, the setTimeout'd function alerts undefined in Chrome. If you know of a service that can simulate a slow resource (delay it 500ms or something) then I think you can replicate that behavior on every load. That seems to contradict the assertion. – Chris Heald Dec 30 '13 at 01:32
  • @ChrisHeald - OK, I removed that option. Not sure how that's allowed to happen, but apparently it isn't solid so I removed that option. – jfriend00 Dec 30 '13 at 01:34
  • I suspect it's because individual – Chris Heald Dec 30 '13 at 01:36
  • Very nice self closing function there. Thanks for the idea! – Yuji 'Tomita' Tomita Dec 30 '13 at 06:48