I was looking through the source of jQuery (specifically the queue() function) and saw that in just puts the function into a the .data() object associated with that element:
queue: function( elem, type, data ) {
var q;
if ( elem ) {
type = ( type || "fx" ) + "queue";
q = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
if ( !q || jQuery.isArray(data) ) {
q = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
q.push( data );
}
}
return q || [];
}
}
Now looking at the ._data which is just .data() with a fourth argument of true, where are any timers or animations being set? Or any function calls for that matter:
data: function( elem, name, data, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var privateCache, thisCache, ret,
internalKey = jQuery.expando,
getByName = typeof name === "string",
// We have to handle DOM nodes and JS objects differently because IE6-7
// can't GC object references properly across the DOM-JS boundary
isNode = elem.nodeType,
// Only DOM nodes need the global jQuery cache; JS object data is
// attached directly to the object so GC can occur automatically
cache = isNode ? jQuery.cache : elem,
// Only defining an ID for JS objects if its cache already exists allows
// the code to shortcut on the same path as a DOM node with no cache
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
isEvents = name === "events";
// Avoid doing any more work than we need to when trying to get data on an
// object that has no data at all
if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
return;
}
if ( !id ) {
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
elem[ internalKey ] = id = ++jQuery.uuid;
} else {
id = internalKey;
}
}
if ( !cache[ id ] ) {
cache[ id ] = {};
// Avoids exposing jQuery metadata on plain JS objects when the object
// is serialized using JSON.stringify
if ( !isNode ) {
cache[ id ].toJSON = jQuery.noop;
}
}
// An object can be passed to jQuery.data instead of a key/value pair; this gets
// shallow copied over onto the existing cache
if ( typeof name === "object" || typeof name === "function" ) {
if ( pvt ) {
cache[ id ] = jQuery.extend( cache[ id ], name );
} else {
cache[ id ].data = jQuery.extend( cache[ id ].data, name );
}
}
privateCache = thisCache = cache[ id ];
// jQuery data() is stored in a separate object inside the object's internal data
// cache in order to avoid key collisions between internal data and user-defined
// data.
if ( !pvt ) {
if ( !thisCache.data ) {
thisCache.data = {};
}
thisCache = thisCache.data;
}
if ( data !== undefined ) {
thisCache[ jQuery.camelCase( name ) ] = data;
}
// Users should not attempt to inspect the internal events object using jQuery.data,
// it is undocumented and subject to change. But does anyone listen? No.
if ( isEvents && !thisCache[ name ] ) {
return privateCache.events;
}
// Check for both converted-to-camel and non-converted data property names
// If a data property was specified
if ( getByName ) {
// First Try to find as-is property data
ret = thisCache[ name ];
// Test for null|undefined property data
if ( ret == null ) {
// Try to find the camelCased property
ret = thisCache[ jQuery.camelCase( name ) ];
}
} else {
ret = thisCache;
}
return ret;
}
EDIT for zzzzBov:
animate: function( prop, speed, easing, callback ) {
var optall = jQuery.speed( speed, easing, callback );
if ( jQuery.isEmptyObject( prop ) ) {
return this.each( optall.complete, [ false ] );
....
return optall.queue === false ?
this.each( doAnimation ) :
this.queue( optall.queue, doAnimation );
jQuery’s
animatemethod is a complex beast (see reference below). It starts by normalizing the parameters, and then immediately jumps into thedoAnimationfunction which is the callback used by jQuery’squeuemethod. jQuery queue’s animations so they happen in order. The queue doesn’t animate anything by itself, it simply acts as a trigger for the code that performs the animation.At the end of
doAnimation, there is a loop to animate every relevant property in the animation (lines 8576 – 8618, v1.7.2). The first inner-line of the loop instantiates a newjQuery.fxobject:At the end of the loop,
e.custom(...)is called in a couple places. This is the important function. If you look throughjQuery.fx.prototype.custom(see reference below), you’ll find:The line with
setIntervalis where jQuery’s animation heartbeat is started. This points tojQuery.fx.tick(see reference below) which iterates through every timer injQuery.timers. If you look at the snippet above, you’ll notice that part of theifstatement involves pushingtinto the stack of timers.twas set injQuery.fx.prototype.customas:And that right there is where jQuery’s animation happens.
Reference
jQuery’s
animatefunction (lines 8484 – 8627, v1.7.2):Instantiation of
jQuery.fx(line 8577, v1.7.2):jQuery.fx.prototype.custom(lines 8806 – 8836, v1.7.2):jQuery.fx.tick(lines 8949 – 8965, v1.7.2):