Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Notes on Paul Irish's "Things I learned from the jQuery source" casts.

Notes on Paul Irish's "Things I learned from the jQuery source" casts

I watched these videos on 2012/12/14. Today's jQuery version is 1.8.3. So there might be some differences from the original video. I've briefly noted some of the differences between what described in the video and the current stable release. Most code he desribed can be found easily in speed/jquery-basic.js (1.4.2).

Episode 1

2010-06-14 (jQuery 1.4.1)

Anonymous Function

1:16

window object passed into anonymous function, for faster object accessing.

undefined as last parameter, to restore the potential overriden undefined object (e.g. undefined=true "a**hole effect").

(function(window, undefined) {
  // do stuff here
})(window);

Asynchronous Recursion

7:45

setInterval may not work at the time you expected, when the action takes longer than the time you specified.

(function() {
  doStuff();

 // gonna wait doStuff done then
  setTimeout(arguments.callee, 100);
})();

arguments.callee deprecated in ES5 strict mode.

We can use named function:

(function loopback() {
  doStuff();

  // gonna wait doStuff done then
  setTimeout(loopback, 100);
})();

noconflict()

12:34

(didn't get it)

jQuery.props

14:15

This is a bridge for class (DOM: className) and for (DOM: htmlFor), and some downcased-to-camelCase mappings (!)

jQuery.props.dt = "data-type";
$("#something").attr("dt"); // gets its data-type attribute

jQuery.fx.speeds

18:01

"Constant" variable to define animation speed (e.g. fast / slow). jQuery searches values in this object. If not found, it uses _default value.

jQuery.fx.speeds.kilo = 1024; // define a new speed with 1024ms duration
$("#somethings").fadeIn("kilo");

DOM Ready callback

21:21

In jQuery 1.4.x, DOM Ready callback is implemented in jQuery.bindReady. However this function does not exist in 1.8.3 (current stable release). The most similar function is jQuery.ready.promise.

Implemenation details:

  • If DOMContentLoaded event is supported, use it.
  • If document.attachEvent exists (old IEs), use it to listen to onreadystatechange event
  • Otherwise, use doScrollCheck, keeps trying scrolling the document (1ms), until it does not raise exception, then it's ready.

See also:

home-made getScript()

24:41

Outdated. He extracted $.getScript implementation of jQuery 1.4.1. Today's jQuery uses XMLHttpRequest.

The 1.4.x implementation can be found at speed/jquery-basic.js#L5060

Selector Performance

33:23

(may be outdated. See sizzle.js)

In 1.4.x,

$("#id").find("tag.thing")

is faster than

$("#id tag.thing")

because the latter must traverse the tree in JavaScript level instead of DOM level.

Todays' jQuery is Sizzle-driven and may already utilies this shortcut too.

:password is slower than input:password, because the former does traverse through all the possible tags.

jQuery.parseJSON

38:46

jQuery use native parser (window.JSON.parse) if supported.

Otherwise, make a new function and invoke it:

(new Function("return "+ jsonSrc ))();

Note the capital F (not function): it makes a new function object, with source code in the parameter.

Since JSON is the literal representation of a JavaScript Object, the browser can parse the object literal inside the function, and turns it into a real object.

jQuery will check if the string is a real JSON string, otherwise it prints an error message to the console.

See also:

jQuery.unique()

42:15

It uses Sizzle.uniqueSort (same as today's implementation in 1.8.3).

The official document says that it does work for any array element other than DOM Element. It is still documented as "unspported" in today's 1.8.3.

Sorts an array of DOM elements, in place, with the duplicates removed. Note that this only works on arrays of DOM elements, not strings or numbers.

However I've tried non-DOM element scalars in 1.8.3, and it does work for Number, String and true, but not work for null, false and undefined. Objects (non-scalar) with same properties are all kept, duplications are not removed. Though this behavior makes sense, because two individual objects are compared by object address, not its properties.

Example

[tl;dr] $.unique doesn't guarantee correct result if there are items not DOM Element. So do use it for an array containing DOM elements only.

DOM elements:

div = document.createElement("div");
div.innerHTML = '<span class="a"></span><span class="b"></span><span class="c">'
"<span class="a"></span><span class="b"></span><span class="c">"

ar1 = $(div).find("span").get();
//=> [<span class="a"></span><span class="b"></span>,<span class="c"></span>]

ar1.concat($(div).find("span").get());
//=> [<span class="a"></span><span class="b"></span>,<span class="c"></span>,<span class="a"></span><span class="b"></span>,<span class="c"></span>]

jQuery.unique(ar1)
//=> [<span class="a"></span><span class="b"></span>,<span class="c"></span>]

Now let's see scalar values:

var arr;

// Number, true, String works
arr = [1, 2, true, "hi", 3, 3, true, 2, 1, "hi"];
$.unique(arr);
// => [3, "hi", true, 2, 1]

// false and undefined doesn't work
arr = [false, false, undefined, undefined];
$.unique(arr);
// => [false, false, undefined, undefined]

// null doesn't work, and will raise exception if the array contains anything other than null:
arr = [null, null];
$.unique(arr);
// => [null, null]

arr = [null, null, false];
$.unique(arr);
// => TypeError: Cannot read property 'compareDocumentPosition' of null

// Object literals doens't work either, since they're allocated in different memory spaces
arr = [{a:1, b:2}, {a:1, b:2}];
JSON.stringify($.unique(arr));
// => "[{"a":1,"b":2},{"a":1,"b":2}]"

Paul Irish hacked it to make 1.4.1's unique() possible to also deal with non-DOM Element objects, see: How to fulfill your own feature request -or- Duck Punching with jQuery! « Paul Irish

See also:

delay

46:58

In 1.4.1, to delay an animation from starting, this:

$(elem).delay(2000).fadeOut();

doesn't work because there is no queue, which delay requires.

$(elem).queue(function() {
  $(elem).delay(2000).fadeOut();
});

this would work.

I've tried 1.8.3 and it works without .queue wrapping.

jQuery.support (feature detection)

48:12

jQuery Source Modules & Building

49:53

jQuery is constructed with many modules. We can build a jQuery with a small set of modules included. The jQuery team said in mid-2012 that there will be a jQuery build tool. It's been 2 years since Paul Irish's cast.

Nowadays people use jQuery from public CDNs. I think building my own jQuery and deliver it from my server means I cannot leverage public caching for jQuery JavaScript file, and pay delivering fee for it.


Episode 2

2011/01/19

jQuery initialization

1:17

It initializes in a local variable and then assign back to window.jQuery.

dom-dynamic-base detection

(I didn't get it.)

data-something with JSON / literal

5:59

<div data-awesome="true"></div>
<div data-someobj='{ "best": "jQuery"}'></div>

In the source code it uses ?: conditional tree (bad-smell) to see if the string equals to true, false or null literals and return directly. If none matched, it tries to parse it as JSON. If JSON parsing failed, it returns the original string.

Technique about getElements

13:04

IE detection -- Inserts <!--[if IE x]><i></i></![endif]--> into a div, and test if a specifc i tag exist.

This is not how jQuery detects IE, Paul Irish just mentioned a technique used in it.

div.getElementsByTagName('i')

It actually is a NodeList, and updates lively as div element changes.

E.g.:

var ul = document.createElement("ul");
var items = div.getElementsByTagName("li");

ul.innerHTML += "<li>1</li>";
console.log(items); //=> [<li>1</li>]

ul.innerHTML += "<li>2</li>";
console.log(items); //=> [<li>1</li>,<li>2</li>]

Also applies to getElementsByClassName, but does not apply to querySelectorAll.

jQuery.cssHooks

18:07

When getting or setting CSS properties that are unsupported in the browser (e.g. opacity in IE6 or backgroundPositionY in Firefox), jQuery will fallback to browser-specific method according to cssHooks functions.

See also:

.css Auto Vendor-Prefixing

25:45

(I rarely use .css because I usually achieve it by Compass and alternating element's className to change the layout.)

jQuery.fn

26:53

jQuery.fn = jQuery.prototype;

This is why we can define a new plug-in by

jQuery.fn.myPlugin = function() {};

See also:

Ace writeup. :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment