Paul Irish's jQuery Craziness

This free-form talk was given by Paul Irish (see paulirish.com) at the 2010 jQuery conference in Mountain View, California.

Some sites Paul has done:

  • Moderniszr.com
  • css3please.com
  • html5readiness.com: A visualization of which HTML5 features are supported and which have less support.

I'm going to call this talk

10 things I learned from the jQuery source

In a lot of ways, jQuery is like a black box. We use it, but we don't find out what's inside of it, even though there's a lot of value in that.

The first thing is how it starts:


(function () {
...
})()

Is a function that runs itself immediately. Facebook uses:


!function () {
...
}()

Which is fine. That stuff will work. We use this a lot:


(function(window,document,undefined){
...
})(this,this.document);

That allows the argument names to be munged when it goes through a minifier, to be come a, b, c (for example.) So every time window and document are referenced they are munged, so we get some performance benefits there.

Also, it's faster to not refer to these values out of scope.

Here's something you shouldn't do:


undefined = true;

Passing undefined as the last argument protects it from jerks.

There's one related pattern that I want to touch on here. This concept of a self-invoking anonymous function can be used for recursion:


(function(){
if(condition){
....
} else {
setTimeout(arguments.callee,1000);
}
})()

We're defining this function and we're immediately going to run it; if our condition isn't met, we're going to continue to loop and see if our condition has been reached. arguments.callee is no more in ECMAscript 5, so we'll use:


(function funfunc(){
if(condition){
....
} else {
setTimeout(funfunc,1000);
}
})()

At some point, you might use setInterval() to poll a function every 50ms, or something. It's possible in that case that there's not enough time to complete what you're trying to do before the time is up to do it again.

In short loops, it's better to just do setTimeout to give it some breathing room.

If you want to find a function in the jQuery source, look for functionName: with a colon at the end, like that.

jQuery.props

If you do:


jQuery('div#mydiv').attr('class')

By default, it checks to see if the attribute exists in jQuery.props. Let's say you're doing some aria work, you can type:


jQuery.props['aeb'] = 'aria-enabled-by';
jQuery('div#mydiv').attr('aeb,'whatever')

Imagine you're trying to remember the speeds. It turns out the default speed isn't 'normal' or 'medium', it's '_default'. When a speed comes in to animate, it checks to see if the speed exists in its speed list; otherwise, it uses default. You could pass in 'totallymediumamirite' and it would be the same as '_default'.

Sometimes, you'll do an animation, but it looks choppy in IE 6. If you double the duration of that animation, it will take longer, but it won't be choppy.


$('div.animate').animate();

So try checking the browser type and changing the default speed:


$('div.animate').animate();
jQuery.fx.speeds._default = ($.browser.msie && ....etc

Duck Punching with jQuery

You can define a brand new ready method, pass the old one as an argument, and modify the definition to pass in these arguments. This enables you to pass in:

$(function($,window,document,undefined){ alert(doocument) };

Convenience Methods

The new documents refer to these as Shorthand Methods. Before, they were just the methods of the API. The reason is because of how they are defined.

We're going to steal jQuery's getScript() and use it on our own. One reason you might want to do this is if you have a bookmarklet, which has a limit on how long it can be; you want to know when it's done loading so that you can fire off the stuff inside.

Selecting by ID

Selecting by ID is pretty easy, e.g. $('#myid') will match quickly because it doesn't have to go far down the code path to match in a regular expression before it does a getElementById(). However something like $('div#myid') won't match this regular expression, and so it will evaluate a lot slower.

Also, it turns out out that $(':password') is actually evaluated as $('*:password'). It grabs every element, then it checks to see if any of those have a type of password. If you can specify the tag name, e.g. $('input:password') then do it!

parseJSON

This asks if what is coming in is valid? You can't have date elements and objects in JSON for it to be JSON.

jQuery noop

If you want to pass a callback that does nothing, jQuery.noop is good for that.

jQuery unique

This was a bummer for me when I tried to de-dupe an array. It turns out you can't use it on Strings or numbers, only DOM elements. It turns out unique just maps to Sizzlesort.

So I rewrote this so that if it's an element we'll call the normal sort, but if it's not then it goes through and de-dupes the array. Now when you pass in an array or an object it works the way that you expect.

You can do this yourself. It's safe... if you do it right!

delay()

By default, delay adds to the animation queue.


$(elem).fadeIn().delay(2000).load('awesome.html #thing');

That won't work, because load won't go into the animation queue.

jQuery source

Briefly, the source is on github, composed of all these little modules. These modules are constructed into the file that we all use. It's 'compiled' by an intro file.

If you're not using some of the functions, you can actually delete the module files and re-roll it yourself.

If you want to check out the full, un-minified source of jQuery, check it out at: bit.ly/jqsource

Add it to your bookmarks bar, and go to the source and look at it instead of looking at the documentation. It's truer than the documentation: you can't argue with what you're looking at!

Thanks to Paul! My head hurts.

Did you enjoy this post? Please spread the word.