88

From the jQuery API docs site for ready

All three of the following syntaxes are equivalent:

  • $(document).ready(handler)
  • $().ready(handler) (this is not recommended)
  • $(handler)

After doing homework - reading and playing with the source code, I have no idea why

$().ready(handler) 

is not recommended. The first and third ways, are exactly the same, the third option calls the ready function on a cached jQuery object with document:

rootjQuery = jQuery(document);
...
...

// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
    return rootjQuery.ready( selector );
}

But the ready function has no interaction with the selector of the selected node elements, The ready source code:

ready: function( fn ) {
    // Attach the listeners
    jQuery.bindReady();
        // Add the callback
    readyList.add( fn );
        return this;
},

As you can see, it justs add the callback to an internal queue( readyList) and doesn't change or use the elements in the set. This lets you call the ready function on every jQuery object.

Like:

  • regular selector: $('a').ready(handler) DEMO
  • Nonsense selector: $('fdhjhjkdafdsjkjriohfjdnfj').ready(handler) DEMO
  • Undefined selector:$().ready(handler) DEMO

Finally... to my question: Why $().ready(handler) is not recommended?

gdoron
  • 147,333
  • 58
  • 291
  • 367
  • I wasn't aware that `$(document).ready(handler)` could be short-handed to `$(handler)`, that's quite nice. Good question. – Richard May 25 '12 at 11:09
  • 60
    @ChaosPandion: To me it seems like he strives to understand the tools he uses like the back of his hand. I wouldn't exactly call that wasted effort. – Jon May 25 '12 at 11:09
  • 5
    Good question. If anyone is interested, [here is a performance comparison](http://jsperf.com/jq-ready-event)... which shows (in Chrome at least) that the "unrecommended" version is actually quickest. – James Allardice May 25 '12 at 11:10
  • 1
    At some point over the past few big releases, the behavior of `$()` - that is, an "empty" call to jQuery - changed. I suspect that the caveat in the documentation dates from that time. – Pointy May 25 '12 at 11:12
  • @JamesAllardice. thanks for the `jsperf` I can even tell you why, the first check in the jQuery constructor is : `// Handle $(""), $(null), or $(undefined) if ( !selector ) { return this ; } ` – gdoron May 25 '12 at 11:12
  • Perhaps because an empty argument is not readable? `$(document).ready` explains much more than `$().ready`. – Rob W May 25 '12 at 11:14
  • @RobW. How is it any different from `$(handler)` ? `.ready(handler)` is readable enough, and more then `$(handler)`. IMO. – gdoron May 25 '12 at 11:14
  • if that's the case, then the quickest way should be `$.fn.ready.call(null, function(){...});` (because `$.fn.ready` doesn't really use `this`) – ori May 25 '12 at 11:15
  • 5
    A better question is why do these even exist at all, it should be a static method (`$.ready` for example) and not require constructing a jQuery object in the first place. – Esailija May 25 '12 at 11:19
  • @Pointy. I looked at older versions: `// Make sure that a selection was provided. selector = selector || document;` **The question remains...** – gdoron May 25 '12 at 11:25
  • @ori: You don't need `.call()`. Having `null` as the calling context isn't likely to be any better than having `$.fn`. Main point is that `.ready()` doesn't rely on any sort of DOM element being in the jQuery object, or for that matter doesn't (currently) rely on there *being* a jQuery object at all. –  May 27 '12 at 02:17
  • 2
    @Esailija makes the best point of all. Unless jQuery plans to provide some sort of `.ready()` capability for individual elements, there should be no reason to construct a jQuery object. –  May 27 '12 at 02:19
  • @JamesAllardice. I got an official answer to the question. You can [read it here](http://stackoverflow.com/a/10777211/601179). – gdoron May 28 '12 at 06:23
  • @gdoron - Nice one! +1 to your answer. Good to hear the official word on it. – James Allardice May 28 '12 at 07:25
  • @Esailija. Ohh, I forgot to tell you (and I didn't add it to the question so it won't look too much like a chat) the problem with `$.ready` is that it's already in use internally, be he admit it's the best design for the ready event. – gdoron May 29 '12 at 17:30
  • 1
    @ChaosPandion I avoid `$(handler)` because its not readable. It blows the mind of people new to jQuery. How are you supposed to search for documentation on a method with no name? –  May 29 '12 at 17:45
  • @JustinY - How exactly is that any different from `$(selector)`? I started with `$(document).ready(handler)` and *"graduated"* to `$(handler)` as soon as I found out it existed. Quite frankly if you don't have the ability to find out what that means I don't really want you to touch my code base. Come get a lesson or two from me first. – ChaosPandion May 29 '12 at 20:51
  • @JustinY - Come to think of it though I was not aware of `$.ready(handler)` which seems like a better way to offer up the API. I may be changing my practices to use that. – ChaosPandion May 29 '12 at 20:54
  • 2
    @ChaosPandion. You can't use that... `$.ready` is already taken by an internal jQuery function, search the source code for `ready:`. – gdoron May 29 '12 at 20:59
  • @gdoron - I realize that now after I reread your answer. The wording subtly lead me to believe that this was a new feature being added. – ChaosPandion May 29 '12 at 21:00

6 Answers6

89

I got an official answer from one of the jQuery developers:

$().ready(fn) only works because $() used to be a shortcut to $(document) (jQuery <1.4)
So $().ready(fn) was a readable code.

But people used to do things like $().mouseover() and all sorts of other madness.
and people had to do $([]) to get an empty jQuery object

So in 1.4 we changed it so $() gives an empty jQuery and we just made $().ready(fn) work so as not to break a lot of code

$().ready(fn) is literally now just patched in core to make it work properly for the legacy case.

The best place for the ready function is $.ready(fn), but it's a really old design decision and that is what we have now.


I asked him:

Do you think that $(fn) is more readable than $().ready(fn) ?!

His answer was:

I always do $(document).ready(fn) in actual apps and typically there's only one doc ready block in the app it's not exactly like a maintenance thing.

I think $(fn) is pretty unreadable too, it's just A Thing That You Have To Know Works™...

Community
  • 1
  • 1
gdoron
  • 147,333
  • 58
  • 291
  • 367
  • 1
    Makes sense, jQuery is pretty serious about backwards compability – Esailija May 27 '12 at 21:22
  • @Esailija: If they were that serious, they wouldn't have switched the behavior of `$()` in the first place *(as goofy as that behavior may have been)*. On the other hand, you're right. They're not always so inclined to make breaking changes, as was shown when they tried to change `.attr()`, then did a quick revert a few days later. This has bound them to some of their unfortunate early (and midlife) design decisions. –  May 27 '12 at 23:19
  • 3
    @gdoron +1 for getting it straight from the horse's mouth. –  May 27 '12 at 23:20
  • 2
    @gdoron +1 for getting the real answer. And, yes, we've been quite near in our perceptions. – VisioN May 28 '12 at 08:13
  • " _it's just A Thing That You Have To Know Works_ ™..." Well so are `$(selector[, context])` and `$(html[, ownerDocument])`. In fact, you might as well just use `jQuery()` instead of `$()` if having-to-know-it-works is the issue. Or why even use jQuery at all? – JAB Sep 18 '13 at 16:57
11

Since the different options do pretty much the same thing as you point out, it's time to put on the library writer hat and make some guesses.

  1. Perhaps the jQuery people would like to have $() available for future use (doubtful since $().ready is documented to work, even if not recommended; it would also pollute the semantics of $ if special-cased).

  2. A much more practical reason: the second version is the only one that does not end up wrapping document, so it's easier to break when maintaining the code. Example:

    // BEFORE
    $(document).ready(foo);
    
    // AFTER: works
    $(document).ready(foo).on("click", "a", function() {});
    

    Contrast this with

    // BEFORE
    $().ready(foo);
    
    // AFTER: breaks
    $().ready(foo).on("click", "a", function() {});
    
  3. Related to the above: ready is a freak in the sense that it's (the only?) method that will work the same no matter what the jQuery object wraps (even if it does not wrap anything as is the case here). This is a major difference from the semantics of other jQuery methods, so specifically relying on this is rightly discouraged.

    Update: As Esailija's comment points out, from an engineering perspective ready should really be a static method exactly because it works like this.

Update #2: Digging at the source, it seems that at some point in the 1.4 branch $() was changed to match $([]), while in 1.3 it behaved like $(document). This change would reinforce the above justifications.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • I have never seen code like this though, the old idiom was `$(document).ready( function(){ //your code here } );` – Esailija May 25 '12 at 11:23
  • I couldn't understand the second update, can you elaborate some more please? I searched for older versions, but couldn't find any difference for this empty jQuery object issue. – gdoron May 25 '12 at 11:39
  • @gdoron: I mean the change from `selector = selector || document` to `if(!selector) return this`. – Jon May 25 '12 at 12:06
4

I would say its simply the fact that $() returns an empty object whereas $(document) does not so your applying ready() to different things; it still works, but I would say its not intuitive.

$(document).ready(function(){}).prop("title") // the title
$().ready(function(){}).prop("title")  //null - no backing document
Alex K.
  • 171,639
  • 30
  • 264
  • 288
  • 1
    Yes, the same perceptions are in [this answer](http://stackoverflow.com/a/2384214/1249581). So the changes took place in version 1.4, which released new return policy. – VisioN May 25 '12 at 11:28
  • I've never seen someone changing the document title while attaching ready callback, And **you can simply do it with vanilla js without jQuery**. I don't say it not might be the answer, but it's not a good reason. – gdoron May 25 '12 at 11:34
  • Well no document properties or methods can be chained via `$()` – Alex K. May 25 '12 at 11:38
  • 2
    @AlexK. his point was that nobody in reality actually chains after `.ready` because it's a well established idiom not to. Sure there is a theoretical chance of someone doing this but I have never seen code doing that (which isn't a good argument but you know :D). – Esailija May 25 '12 at 11:39
  • 2
    Maybe because of this *theoretical chance* method is *not recommended*, since it has different behavior in different releases. – VisioN May 25 '12 at 11:42
  • @Esailija. I think it is fine not changing the document, you didn't pass the document as the "backing field" why should it change it? – gdoron May 25 '12 at 11:43
  • @VisioN. I thought so too until I got an official answer from a jQuery developer. [Read the answer](http://stackoverflow.com/a/10777211/601179) – gdoron May 28 '12 at 06:21
3

More than likely this is just a documentation bug and should be fixed, the only downside to using $().ready(handler) is it's readability. Sure, argue that $(handler) is just as unreadable. I agree, that's why I don't use it.

You can also argue that one method is faster than another. However, how often do you call this method enough times in a row on a single page to notice a difference?

Ultimately it comes down to personal preference. There is no downside to using $().ready(handler) other than the readability argument. I think the documentation is miss-leading in this case.

Kevin B
  • 94,570
  • 16
  • 163
  • 180
  • **+1!** You were absolutely right! You will love to read the official jQuery answer. I added it as an answer. – gdoron May 28 '12 at 08:27
2

Just to make it patently obvious that there is some inconsistency in the three, plus I added the fourth often used form: (function($) {}(jQuery));

With this markup:

<div >one</div>
<div>two</div>
<div id='t'/>

and this code:

var howmanyEmpty = $().ready().find('*').length;
var howmanyHandler = $(function() {}).find('*').length;
var howmanyDoc = $(document).ready().find('*').length;
var howmanyPassed = (function($) { return $('*').length; }(jQuery));
var howmanyYuck = (function($) {}(jQuery));
var howmanyYuckType = (typeof howmanyYuck);

$(document).ready(function() {
    $('#t').text(howmanyEmpty + ":" + howmanyHandler + ":" 
        + howmanyDoc + ":" + howmanyPassed + ":" + howmanyYuckType);
});

The displayed results of the div from the last statement are: 0:9:9:9:undefined

SO, only the Handler and Doc versions are consistent with the jQuery convention of returning something of use as they get the document selector and with the Passed form you must return something (I wouldn't do this I would think, but put it in just to show "inside" it has something).

Here is a fiddle version of this for the curious: http://jsfiddle.net/az85G/

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
  • I can't see what's the problem with that it's not finding anything, the context you gave for the jQuery is `null` so `.find('*').length` return **0**. Do you find something bad with this (obvious) behavior? – gdoron May 25 '12 at 13:32
  • @gdoron - I don't find anything bad with this behavior, I just wanted to point out the difference compared to when it DOES have a selector that is NOT null - note that this empty selector is likely why the "faster" comments are noted elsewhere as it has a smaller object to process, but DOES return an object and not "undefined" in that instance. I DO really like the question though and did upvote it :) – Mark Schultheiss May 25 '12 at 13:35
  • The reason is why it's faster is, because the first "break" condition of the ctor is `if(!selector) return this` if you give something else, there are `regex` and other things going on... Thanks for kindly words... I think I might ask jQuery team to answer this(hell, it's not my library `:-)`). – gdoron May 25 '12 at 13:38
  • Yep, I have not studied this particular part of the code base, I have hacked the core to put in interum fixes for bugs but not that part of it. I DO prefer to see the `jQuery(document).ready(function(){});` form in our code base at present as there are differing levels of jQuery expertise and it is "most obvious" to new folks that it IS an event handler function for jQuery. – Mark Schultheiss May 25 '12 at 13:50
0

I think this is really more for readability than anything else.

This one isn't as expressive

$().ready(handler);

as

$(document).ready(handler)

Perhaps they are trying to promote some form of idiomatic jQuery.

Hyangelo
  • 4,784
  • 4
  • 26
  • 33