6

The situation where jQuery is loaded late on the page but javascript that relies on jQuery being available is loaded before jQuery is a pretty common scenario, especially if you follow the practice of putting your scripts closer to </body>.

So basically I want to go from this:

<script>
  someFunctionThatUsesLateJQuery(){ [code that relies on jQuery] }
</script>

...

<script src="jquery.js"></script>
<script>
  $(function(){
    someFunctionThatUsesLateJQuery();
  });
</script>

To something like this:

<script>
  _$.ready(function(){ [code that relies on jQuery] });
</script>

...

<script src="jquery.js"></script>

Much like asynchronous stats tracking (á la Google Analytics), is there anything out there that allows you to register javascript function calls to be executed once jQuery is loaded without getting the dreaded $ is undefined error?

I mean for this to happen without registering and deregistering timeouts/intervals.

Has there ever been/is there the possibility of adding some sort of pre-registration variable to jQuery that it recognises and handles once it's loaded?

Use case:

It's worth noting that the specific use-case I've got is a drop-in JS widget, where I want some DOM manipulation to happen at the scene of the <script> placement, which therefore has the very real possibility of appearing before jQuery has loaded in the case of jQuery loading happening near </body>.

I then don't want to burden the user further by requiring them to register a specific function call at the correct point in code execution (which will be dependent on their implementation)... I want it to "just work"

simonhamp
  • 3,078
  • 1
  • 20
  • 26
  • What sort of DOM manipulation do you want to occur "immediately" that can't wait for jQuery to load, and would also then incur a second pass through your DOM? – krillgar Jul 23 '14 at 12:06
  • I don't want the DOM manipulation to occur immediately. I just want the manipulation to take place when jQuery is ready without having to burden the "developer" with writing a separate call – simonhamp Jul 23 '14 at 12:12
  • 1
    Then why have the first ` – krillgar Jul 23 '14 at 12:16
  • 1
    If you check my use-case, it's really just about neatness and being able to guarantee working code in a single ` – simonhamp Jul 23 '14 at 12:20

4 Answers4

0

You could try to use common and popular solution when all your function calls moved to data-attributes. Then, when jQuery is loaded, you go throw all the DOM elements with such attributes and run this functions. I.e. instead of function call you add data-type='load' data-function='YourNamespace.yourFunctionName' and after window.load event you select all the elements by $('[data-type="load"]'), iterates them, and perform function calls.


UPD: Guess, async function queuing pattern could be usefull to read about: What's the name of Google Analytics async design pattern and where is it used?

Community
  • 1
  • 1
Dmitry Volokh
  • 1,630
  • 16
  • 28
  • As example you can see my tiny JSLib I had wrote for my past project https://github.com/davolokh/jsim – Dmitry Volokh Jul 23 '14 at 11:35
  • Thanks Dmitry, that's an interesting approach. Unfortunately it still requires that the code that actually runs to be parsed _after_ jQuery has loaded, which is what I'm trying to avoid – simonhamp Jul 23 '14 at 11:56
  • @Simon I'm almost sure that you'll not find the solution without post-processing your code after jQuery is loaded. Somehow or other you'll need to figure out what had happend before the lib was loaded and afterwards you must do smth. – Dmitry Volokh Jul 23 '14 at 12:06
  • I agree. The point of the question isn't necessarily to find a solution, but to highlight the need for one. I believe this needs to be baked into jQuery. Admittedly SO is not necessarily the place to do that, but I'm testing the waters and checking it hasn't already been done. – simonhamp Jul 23 '14 at 12:10
  • 1
    @Simon In that case the best option is to submit an issue to jQuery directly. https://github.com/jquery/jquery – Dmitry Volokh Jul 23 '14 at 12:15
  • @Simon I found some info about async function queuing. Just added to the answer above. Guess, it could be usefull. – Dmitry Volokh Jul 24 '14 at 09:17
0

As suggested here by rich.okelly you could try this:

(function() {
    var runMyCode = function($) {
        // jquery-dependent code here
        $("#foo").data('bar', true);
    };

    var timer = function() {
        if (window.jQuery && window.jQuery.ui) {
            runMyCode(window.jQuery);
        } else {
            window.setTimeout(timer, 100);
        }
    };
    timer();
})();

I also found a more complex solution, but I personally prefer the above solution over this solution.

Update without timeout:

<script>
var runMyCode = function($) {
    // jquery-dependent code here
    $("#foo").data('bar', true);
};
</script>

...

<script src="jquery.js"></script>
<script>
    $(function(){
        runMyCode(window.jQuery);
    });
</script>
Community
  • 1
  • 1
NullPointer
  • 442
  • 3
  • 7
  • Setting timeout isn't very good idea, but the second link you provided looks nice from the first view. – Dmitry Volokh Jul 23 '14 at 11:47
  • Thanks for the suggestion, but I did specifically say that I _didn't_ want to use a timer. The other solution you link to is basically my idea, but I'm wondering if there is any plan to include this within jQuery itself – simonhamp Jul 23 '14 at 11:48
  • Sorry for the timeout function, what about the second solution where you pass in jQuery to a predefined function after jQuery is loaded? – NullPointer Jul 23 '14 at 11:59
  • As I said, that's almost exactly what I'm proposing, so that's a great find. The problem is it's not a part of jQuery so I can't rely on that being available (check latest update re use-case) Edit: just realised you meant your second updated suggestion. As krillgar said, it's not really of any benefit and still has the same issue. – simonhamp Jul 23 '14 at 12:04
  • What would be the advantage of passing jQuery as a parameter? At the point `runMyCode()` is called, window.jQuery is already in the global scope. – krillgar Jul 23 '14 at 12:05
  • 1
    Oh I see, you want any (also external) jQuery dependent code to just work without modifications; in that case the closest solution I can think of now is the one described in the link. I'm curious to find out how you will solve this problem! – NullPointer Jul 23 '14 at 12:14
0

If you inject the jQuery script include, then you can listen for the onload event.

function loadJS(url, onloadCallback, elId){
//Inject script include into HEAD
    var scriptEl = document.createElement('script');
    scriptEl.type = 'text/javascript';
    scriptEl.src = url;
    if(elId)scriptEl.id = elId;
    document.getElementsByTagName('head')[0].appendChild(scriptEl);

    if(onloadCallback){
        scriptEl.onload = scriptEl.onreadystatechange = function(){
            if(!scriptEl.readyState || (scriptEl.readyState === 'complete' || scriptEl.readyState === 'loaded')){
                onloadCallback();
                scriptEl.onload = scriptEl.onreadystatechange = null;
            }
        }
    }
}

loadJS("http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js", function(){
    console.log( window.$ );
});
robC
  • 2,592
  • 1
  • 23
  • 28
0

Perhaps I don't understand the use case fully, but RequireJS sounds like it might be a good option for you:

https://github.com/requirejs/example-jquery-shim

define(["jquery", "jquery.alpha", "jquery.beta"], function($) {
    //the jquery.alpha.js and jquery.beta.js plugins have been loaded.
    $(function() {
        $('body').alpha().beta();
    });
});
Drew Delano
  • 1,421
  • 16
  • 21