How can I use yepnope.js with $(document).ready() effectively?
Asked Answered
H

5

15

I have been implementing the yepnope script loader as part of the modernizr.js library. I have successfully got jQuery to load and jQuery dependent scripts afterwards. I am new to asynchronous loading of resources, so it's a bit new to me. I have been searching around, but haven't had much luck with the following.

My question is what are your opinions on how to effectively replace the functionality of $(document).ready() when working with the yepnope.js framework.

My theory was to create a appropriately named function in my base library and then set that variable on my pages to an anonymous function containing my existing $(document).ready() code. This variable would then be called by yepnope after all the scripts had loaded in the complete callback.

Would you agree that this is a good way of doing it, or am I approaching this entirely the wrong way?

(For those unaware, the asynchronous nature of yepnope.js means that the document calls $ or jQuery before the yepnope loader has finished, throwing a "$ is undefined" error <- please correct me if that is wrong.)

First question, hope it's a good one.

Humeral answered 14/4, 2011 at 19:3 Comment(1)
To just clarify, I am integrating this into a site with a lot of existing js files that apply to individual pages, that depend on document.ready(). Perhaps this is the wrong way to do it - I'd like to hear all your views.Humeral
S
11

If load jQuery without yepnope isn't a problem for you, there is a easier way to do.

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

<script>
    $.holdReady(true);

    yepnope.load({
        load: [
            'placeholder.js',
            'jquery-ui.min.js'
        ],
        complete: function (){
            $.holdReady(false);
        }
    });
</script>
Scriber answered 6/3, 2012 at 11:8 Comment(3)
Thank you! I had no idea jQuery had this feature. Much simpler and more elegant solution.Eleonoraeleonore
do I put $(document).ready(); right below this? does it need to be in it's separate <script> tag?Gavrielle
You should put $(document).ready() below $.holdReady() in separate <script> tag or notScriber
R
11

This is the technique I use. It allows me to sprinkle $(document).ready() style calls wherever I like. Using this method, you can take a site that already uses jQuery and has existing $(document).ready() calls, and easily retrofit yepnope.

First, add this line of JS, preferably in the document head, before any javascript that calls $(document).ready():

<script>
    var docready=[],$=function(o){function r(fn){docready.push(fn);}if(typeof o === 'function') r(o);return{ready: r}};
</script>

Then, have your yepnope jQuery test object set similar to this:

yepnope({
    load: '//ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js',
    complete: function() {
        $ = jQuery;         
        for(n in docready) $(document).ready(docready[n]);
    }
});

We create a fake $(document).ready() before jQuery loads. This stores every $(document).ready() call in an array, docready. Then, once jQuery has loaded, we overwrite our temporary $ object with the now loaded real jQuery object. Then, we iterate through all the stored $(document).ready() calls, and execute them for real.

UPDATED: improved version from Chris Jones that also covers $(function() {}) style invocations.

Rasure answered 19/12, 2011 at 20:37 Comment(3)
Hi, I used the method you describe and placing the yepnope just before the closing body tag every thing works fine. But: I load the jquery.min.js somewhere before, so using this method will load an unnecessary jquery again? Regards AndreaSheelagh
Hi Andrea - if you have already tried to load jQuery beforehand, you could either 1) remove the call from yepnope that loads jquery and just leave your other scripts, 2) change the yepnope block that loads jQuery to this: { test: typeof jQuery == 'undefined', yep: '//ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js', complete: function() { $ = jQuery; for(n in docready) $(document).ready(docready[n]); } } 3) remove your earlier synchronous load of jQuery and wrap any calls that use jQuery in $(document).ready(function() { ... }) blocks.Rasure
Nice solution. I will definitely try it as well. But, how to handle JQuery plugins which are registered through JQuery.fn or $.fn in this scenario? The latter being the worst I think, because those registrations will succeed and be overwritten afterwards ;) ... Or am I missing some point here about plugins making them still working like expected?Infamous
S
11

If load jQuery without yepnope isn't a problem for you, there is a easier way to do.

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

<script>
    $.holdReady(true);

    yepnope.load({
        load: [
            'placeholder.js',
            'jquery-ui.min.js'
        ],
        complete: function (){
            $.holdReady(false);
        }
    });
</script>
Scriber answered 6/3, 2012 at 11:8 Comment(3)
Thank you! I had no idea jQuery had this feature. Much simpler and more elegant solution.Eleonoraeleonore
do I put $(document).ready(); right below this? does it need to be in it's separate <script> tag?Gavrielle
You should put $(document).ready() below $.holdReady() in separate <script> tag or notScriber
S
1

script tags are loading synchronously - so if you put your yepnope in a js file and load it via script tag:

   <script type="text/javascript" src="/my-yepnope-stuff.js"></script>
</body>

right before the closing body tag you can be quite sure to be at $(document).ready() state.

What you need to answer for yourself is whether it makes sense to force yepnope to load in a $(document).ready() fashion, as its main purpose is to break the synchronous loading order of script tags in the first place.

Sabrinasabsay answered 17/4, 2011 at 15:7 Comment(1)
My issue is that, in some cases, I have to test for document ready, as I use third party control libraries (telerik). Their functions return nulls if controls can't be found (i.e. that are not loaded yet). Other than this, I'd like to work towards having asynchronous script loading.Humeral
H
0

Using guidance from @ezmilhouse, I thought about the best way to achieve what I was after while still keeping compatibility with our older code.

My solution was to set up my yepnope scriptloader to load all necessary scripts in a hierarchical fashion, based on their individual dependencies. Once all scripts are loaded, you can use the complete property of my call to yepnope to call my ready function. This meant that the document was effectively ready and the code would work with no issues.

I also moved my js to the base of my pages (something that I should have done a long time ago, but we had a lot of legacy pages! :) )

Here is an example (using false libray/script names for illustration purposes only):

yepnope({
    test: baseLib.debug,
    yep: { "max": "/version2/res/jquery/jquery-1.5.2.js" },
    nope: { "min": "/version2/res/jquery/jquery-1.5.2.min.js" },
    callback: {
        "max": function (url, result, key) {
            baseLib.Log("jQuery full loaded.");
        },
        "min": function (url, result, key) {
            baseLib.Log("jQuery min loaded.");
        }
    },
    complete: function () {
        if (window.$) {
            yepnope({
                test: base.debug,
                yep: {
                   "anotherscript": "script/url/here.js",
                   "anotherscript2": "script/url/here2.js"
                },
                nope: {
                    "anotherscript": "script/url/here-min.js",
                    "anotherscript2": "script/url/here2-min.js"
                },
                both: {
                    "anotherscript3": "script/url/here3.js"
                },
                callback: {
                    "anotherscript": function (url, result, key) {
                        baseLib.Log("anotherscript " + (result ? "Max" : "Min") + " loaded.");

                    },
                    "anotherscript2": function (url, result, key) {
                        baseLib.Log("anotherscript2 " + (result ? "Max" : "Min") + " loaded.");
                    },
                    "anotherscript3": function (url, result, key) {
                        baseLib.Log("anotherscript3 loaded.");
                    }
                },
                complete: function () {
                    baseLib.Log("Scripts Loaded");
                    baseLib.Page.Ready();
                }
            });

        }
        else {
            baseLib.Log("Could not load jQuery. No further jQuery dependent files loaded.", "error");
        }
    }
});

In my page js I will assign a function to baseLib.Page.Ready that will then be called by yepnope on complete.

Humeral answered 13/5, 2011 at 10:13 Comment(0)
S
0

I think that Alex Sexton solution would be correct :

yepnope({
    load: '//ajax.googleapisOFFLINE.com/ajaxX/libs/jquery/1.7.1/jquery.min.js',
    callback: function () {
        if (!window.jQuery) {
            yepnope('/js/jquery-1.7.1.min.js');
        }
    },
    complete: function () {
      $(function(){
        $("div.whatever").css("color","red");
      });
    }
});
Sikh answered 26/7, 2013 at 19:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.