Recent Changes to
jQuery’s Internals
John Resig

September 12, 2009
No More Browser Sniffing


✤
    As of jQuery 1.3

✤
    jQuery.browser is now deprecated

✤
    All uses of jQuery.browser have been removed from core.

✤
    jQuery.support replaces it.
jQuery.support




✤   div.innerHTML = '   <link/><table></table><a href="/a"
    style="color:red;float:left;opacity:.5;">a
      </a><select><option>text</option></select>';
jQuery.support
    ✤    jQuery.support = {

!   !     // IE strips leading whitespace when .innerHTML is used
!   !     leadingWhitespace: div.firstChild.nodeType == 3,

!   !     // Make sure that tbody elements aren't automatically inserted
!   !     // IE will insert them into empty tables
!   !     tbody: !div.getElementsByTagName("tbody").length,

!   !     // Make sure that link elements get serialized correctly by innerHTML
!   !     // This requires a wrapper element in IE
!   !     htmlSerialize: !!div.getElementsByTagName("link").length,

!   !     // Get the style information from getAttribute
!   !     // (IE uses .cssText insted)
!   !     style: /red/.test( a.getAttribute("style") ),

!   !     // Make sure that URLs aren't manipulated
!   !     // (IE normalizes it by default)
!   !     hrefNormalized: a.getAttribute("href") === "/a",

!   !     // Make sure that element opacity exists
!   !     // (IE uses filter instead)
!   !     opacity: a.style.opacity === "0.5",

!   !     // Verify style float existence
!   !     // (IE uses styleFloat instead of cssFloat)
!   !     cssFloat: !!a.style.cssFloat,

!   !     // Will be defined later
!   !     scriptEval: false,
!   !     noCloneEvent: true,
!   !     boxModel: null
!   };
Modularity
✤   Starting in 1.3.3 code is broken up even more

✤   Core has been split into many sub-modules

    ✤   core.js

    ✤   attributes.js

    ✤   css.js

    ✤   manipulation.js

    ✤   traversing.js
Modularity



✤
    Starting to reduce dependencies in-between files

✤
    Makes it easier to dynamically load portions of the library

✤
    (Mobile jQuery!)
Mobile jQuery?

✤
    “Heavy” core.js

    ✤
        ready event

    ✤
        find() (powered by querySelectorAll)

    ✤
        filter() (very basic functionality)

    ✤
        $.getScript() (for loading modules)

✤
    Still experimenting...
Sizzle
✤
    Standalone selector engine, landed in jQuery 1.3.
                                                    Selector          % Used    # of Uses
✤
    Built for selectors that people actually use.   #id               51.290%   1431
                                                    .class            13.082%   365
                                                    tag               6.416%    179
                                                    tag.class         3.978%    111
                                                    #id tag           2.151%    60
                                                    tag#id            1.935%    54
                                                    #id:visible       1.577%    44
                                                    #id .class        1.434%    40
                                                    .class .class     1.183%    33
                                                    *                 0.968%    27
                                                    #id tag.class     0.932%    26
                                                    #id:hidden        0.789%    22
                                                    tag[name=value]   0.645%    18
                                                    .class tag        0.573%    16
                                                    [name=value]      0.538%    15
                                                    tag tag           0.502%    14
                                                    #id #id           0.430%    12
                                                    #id tag tag       0.358%    10
Right to Left

✤
    Most selector engines (including jQuery, pre-1.3) evaluate a selector
    left to right

✤
    “#id div” - finds the element by ID “id” then finds all divs inside of it.

✤
    Sizzle finds all divs then checks to see if theres an ancestor with an id
    of “id”.

✤
    How CSS engines work in browsers.

✤
    Only one query per selector, rest is filtering.
Reducing Complexity
Analyzing Performance

✤
    Optimizing performance is a huge concern: Faster code = happy
    users!

✤
    Measure execution time

✤
    Loop the code a few times

✤
    Measure the difference:

    ✤
        (new Date).getTime();
Stack Profiling



✤
    jQuery Stack Profiler

✤
    Look for problematic methods and plugins

✤
    http://ejohn.org/blog/deep-profiling-jquery-apps/
FireUnit



✤
    A simple JavaScript test suite embedded in Firebug.

✤
    http://fireunit.org/
FireUnit Profile Data


                                       {
   fireunit.getProfile();                    "time": 8.443,
                                           "calls": 611,
                                           "data":[
                                           {
                                             "name":"makeArray()",
                                             "calls":1,
                                             "percent":23.58,
                                             "ownTime":1.991,
                                             "time":1.991,
                                             "avgTime":1.991,
                                             "minTime":1.991,
http://ejohn.org/blog/function-call-         "maxTime":1.991,
                                             "fileName":"jquery.js (line 2059)"
             profiling/
Complexity Analysis
 ✤
     Analyze complexity rather than raw time

 ✤
     jQuery Call Count Profiler (uses FireUnit)
                     Method                           Calls           Big-O

.addClass("test");                            542             6n

.addClass("test");                            592             6n

.removeClass("test");                         754             8n

.removeClass("test");                         610             6n

.css("color", "red");                         495             5n

.css({color: "red", border: "1px solid red"}); 887            9n

.remove();                                    23772           2n+n2

.append("<p>test</p>");                       307             3n
Complexity Analysis

✤
    Reducing call count helps to reduce complexity

✤
    Results for 1.3.3:

                   Method                    Calls        Big-O

          .remove();                   298           3n

          .html("<p>test</p>"); 507                  5n

          .empty();                    200           2n


http://ejohn.org/blog/function-call-
ECMAScript 5 Support


✤
    New in 1.3.3

✤
    Removal of arguments.callee

✤
    Switching from ‘RegExp to ‘new RegExp’

✤
    Using JSON.parse, where available.

    ✤
        Support for json2.js is auto-baked in.
Unified Syntax

✤
    Reusable inline functions and RegExp moved outside.
✤
    var rinlinejQuery = / jQueryd+="(?:d+|null)"/g,


!   rleadingWhitespace = /^s+/,
!   rsingleTag = /^<(w+)s*/?>$/,
!   rxhtmlTag = /(<(w+)[^>]*?)/>/g,
!   rselfClosing = /^(?:abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i,
!   rinsideTable = /^<(thead|tbody|tfoot|colg|cap)/,
!   rtbody = /<tbody/i,
!   fcloseTag = function(all, front, tag){
!   ! return rselfClosing.test(tag) ?
!   ! ! all :
!   ! ! front + "></" + tag + ">";
!   };


✤
    http://docs.jquery.com/JQuery_Core_Style_Guidelines
Type Checks


    String: typeof object === "string"
•   Number: typeof object === "number"
•   Boolean: typeof object === "boolean"
•   Object: typeof object === "object"
•   Function: jQuery.isFunction(object)
•   Array: jQuery.isArray(object)
•   Element: object.nodeType
•   null: object === null
•   undefined: typeof variable === "undefined" or object.prop === undefined
•   null or undefined: object == null
isFunction / isArray


✤
    isFunction overhauled in 1.3, isArray added in 1.3.3
✤



    toString = Object.prototype.toString

    isFunction: function( obj ) {


!   ! return toString.call(obj) === "[object Function]";
!   },

!   isArray: function( obj ) {
!   ! return toString.call(obj) === "[object Array]";
!   },
Core
.selector / .context

✤
    Added in 1.3

✤
    Predominantly used for plugins

✤
    var div = jQuery(“div”);
    div.selector === “div”;
    div.context === document;

✤
    jQuery.fn.update = function(){
       return this.pushStack( jQuery(this.selector, this.context), “update” );
    };
jQuery(...) unified


✤
    In 1.3.3

✤
    jQuery(null), jQuery(false), jQuery(undefined) => jQuery([])

✤
    jQuery() is still the same as jQuery(document) (for now)

✤
    BUT jQuery(“div”) points back to a common jQuery(document) root.

✤
    jQuery(“div”).prevObject === jQuery()
jQuery(“TAG”)



✤
    In 1.3.3

✤
    jQuery(“body”) is now a shortcut for jQuery(document.body)

✤
    jQuery(“TAG”) is optimized (skips Sizzle)
Position

✤
    All in 1.3.3

✤
    .get(-N), .eq(-N)
    Access elements/jQuery sets using negative indices.

✤
    .first(), .last()
    Shortcuts for .eq(0) and .eq(-1)

✤
    .toArray()
    Shortcut for .get()
.data()


✤
    In 1.3.3

✤
    Calling .data() (no args) returns the full data object for an element

✤
    jQuery(“#foo”).data(“a”, 1).data(“b”, 2).data() =>
     { a: 1, b: 2 }
Selectors
Document Order
      if ( document.documentElement.compareDocumentPosition ) {
!     sortOrder = function( a, b ) {
!     !    var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
!     !    if ( ret === 0 ) {
!     !    !    hasDuplicate = true;
!     !    }
!     !    return ret;
!     };
}   else if ( "sourceIndex" in document.documentElement ) {
!     sortOrder = function( a, b ) {
!     !    var ret = a.sourceIndex - b.sourceIndex;
!     !    if ( ret === 0 ) {
!     !    !    hasDuplicate = true;
!     !    }
!     !    return ret;
!     };
}   else if ( document.createRange ) {
!     sortOrder = function( a, b ) {
!     !    var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
!     !    aRange.selectNode(a);
!     !    aRange.collapse(true);
!     !    bRange.selectNode(b);
!     !    bRange.collapse(true);
!     !    var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
!     !    if ( ret === 0 ) {
!     !    !    hasDuplicate = true;
!     !    }
!     !    return ret;
!
}
      };      ✤
                 As of 1.3.2 all selectors return in document order
                 (as if you did getElementsByTagName(“*”))
:hidden / :visible

✤
     Changed in 1.3.2, revised in 1.3.3

✤
     Changed logic of :hidden/:visible to use .offsetWidth === 0
     && .offsetHeight === 0 trick
✤    Sizzle.selectors.filters.hidden = function(elem){


!     var width = elem.offsetWidth, height = elem.offsetHeight,
!     !     force = /^tr$/i.test( elem.nodeName ); // ticket #4512
!     return ( width === 0 && height === 0 && !force ) ?
!     !   true :
!     !   !    ( width !== 0 && height !== 0 && !force ) ?
!     !   !    !   false :
!     !   !    !   !   !!( jQuery.curCSS(elem, "display") === "none" );
    };
ID-rooted Queries

✤
    Changed in 1.3.3

✤
    $(“#id div”) sped up dramatically

✤
    #id is detected and used as the root of the query

✤
    $(“div”, “#id”) --> $(“#id”).find(“div”)

✤
    $(“#id div”) can likely never be as fast as $(“#id”).find(“div”)

    ✤
        $(“#id”) is so hyper-optimized, it’s hard to match raw perf.
DOM Manipulation
HTML Fragments

✤
    Overhauled in 1.3

✤
    .append(“<li>foo</li>”) first converts HTML to a DOM fragment

✤
    Fragments act as a loose collection for DOM nodes

✤
    Can be used to insert many nodes with a single operation

✤
    var fragment = document.createDocumentFragment();
    while ( node.firstChild )
      fragment.appendChild( node.firstChild );
    document.body.appendChild( node );
HTML Fragments (redux.)

✤
    In 1.3.3

✤
    It turns out that using fragments makes it really easy to cache them,
    as well.

✤
    Look for common cases that can be cached (simple, small, HTML
    strings that are built more than once).

✤
    Also optimized $(“<some html>”), cut out some unnecessary
    intermediary code.

✤
    jQuery.fragments[“<some html>”] = fragment;
HTML Fragments

✤
    Surprising change:

✤
    for ( var i = 0; i < 250; i++ ) {
      $(“<li>some thing</li>”).attr(“id”, “foo” + i).appendTo(“ul”);
    }

✤
    Now faster than:

✤
    for ( var i = 0; i < 250; i++ ) {
      $(“ul”).append(“<li id=‘foo” + i + “‘>some thing</li>”);
    }
$(“<div/>”)


✤
    In 1.3:

✤
    $(“<div/>”) was made equivalent to $(document.createElement
    (“div”))

✤
    In 1.3.3:

✤
    Logic for handling createElement moved to jQuery() (performance
    improved!)
append(function(){ })
✤
    In 1.3.3 append, prepend, before, after, wrap, wrapAll, replace, replaceAll all
    take a function as an argument

✤
    Compliments .attr(“title”, function(){}) (and CSS)

✤
    Makes:
    $(“li”).each(function(){
        $(this).append(“My id: “ + this.id);
    });

✤
    Also:
    $(“li”).append(function(){
      return “My id: “ + this.id;
    });
.detach()


✤
    In 1.3.3

✤
    Like .remove() but leaves events and data intact.
✤   detach: function( selector ) {


!   !  return this.remove( selector, true );
!   },
DOM Traversal
.closest()


✤
    Added in 1.3

✤
    $(this).closest(“div”) checks if ‘this’ is a div, if not keeps going up the
    tree until it finds the closest one.

✤
    In 1.3.3

✤
    $(this).closest(“.test”, document.body)
    Prevent the traversal from going too far up the tree.
find() perf

✤
    In 1.3.3

✤
    Reduced the number of function calls - Reduced 16 calls per find() to 5.
✤   find: function( selector ) {


!   !    var ret = this.pushStack( "", "find", selector ), length = 0;

!   !    for ( var i = 0, l = this.length; i < l; i++ ) {
!   !    !    length = ret.length;
!   !    !    jQuery.find( selector, this[i], ret );

!   !    !     if ( i > 0 ) {
!   !    !     !    // Make sure that the results are unique
!   !    !     !    for ( var n = length; n < ret.length; n++ ) {
!   !    !     !    !    for ( var r = 0; r < length; r++ ) {
!   !    !     !    !    !    if ( ret[r] === ret[n] ) {
!   !    !     !    !    !    !    ret.splice(n--, 1);
!   !    !     !    !    !    !    break;
!   !    !     !    !    !    }
!   !    !     !    !    }
!   !    !     !    }
!   !    !     }
!   !    }

!   !    return ret;
!   },
find() perf




✤
    Perf for $(“...”) improved, as well (hooked into rootQuery, uses less
    function calls)
.not() / .filter()



✤
    .not(function(){}) (1.3.3)

✤
    .filter(Element), .filter(function(){}) (1.3.3)

✤
    Full API parity inbetween .not() and .filter()
.index()



✤
    In 1.3.3

✤
    $(“div”).index() - position of element relative to siblings

✤
    $(“#foo”).index(“div”) - position relative to all divs
Events
Live Events


✤
    Super-efficient event delegation - uses .closest(), introduced in 1.3.

✤
    1.3.3 adds context and data object support.

✤
    1.3.3 will ship once “submit”, “change”, and “focus/blur” events
    work in .live() (in all browsers).
.bind() `thisObject`


✤
    In 1.3.3

✤
    You can now bind functions enforcing this `this`

✤
    var obj = { method: function(){} };
    $(“div”).bind( “click”, function(){
       this.objProp = true;
    }, obj );
Dynamic Ready Event



✤
    In 1.3.3

✤
    document.readyState is checked to determine if the body is already
    loaded - if so, no need to wait for ready event.
Method                                      Calls      O(N)
.addClass("test");                                                    542             O(6n)

.addClass("test");                                                    592             O(6n)

.removeClass("test");                                                 754             O(8n)

.removeClass("test");                                                 610             O(6n)

.css("color", "red");                                                 495             O(5n)

.css({color: "red", border: "1px solid red"});                        887             O(9n)

.remove();                                                            23772           O(2n+n2)

.append("<p>test</p>");                                               307             O(3n)

.append("<p>test</p><p>test</p><p>test</p><p>test</p><p>test</p>");   319             O(3n)

.show();                                                              394             O(4n)

.hide();                                                              394             O(4n)

.html("<p>test</p>");                                                 28759           O(3n+n2)

.empty();                                                             28452           O(3n+n2)

.is("div");                                                           110

.filter("div");                                                        216             O(2n)

.find("div");                                                          1564            O(16n)
Method                                    Calls      O(N)
.addClass("test");                                                    2

.addClass("test");                                                    2

.removeClass("test");                                                 2

.removeClass("test");                                                 2

.css("color", "red");                                                 102

.css({color: "red", border: "1px solid red"});                        201           O(2n)

.remove();                                                            299           O(3n)

.append("<p>test</p>");                                               116

.append("<p>test</p><p>test</p><p>test</p><p>test</p><p>test</p>");   128

.show();                                                              394           O(4n)

.hide();                                                              394           O(4n)

.html("<p>test</p>");                                                 100

.empty();                                                             201           O(2n)

.is("div");                                                           109

.filter("div");                                                        216           O(2n)

.find("div");                                                          495           O(5n)
Recent Changes to jQuery's Internals

Recent Changes to jQuery's Internals

  • 1.
    Recent Changes to jQuery’sInternals John Resig September 12, 2009
  • 2.
    No More BrowserSniffing ✤ As of jQuery 1.3 ✤ jQuery.browser is now deprecated ✤ All uses of jQuery.browser have been removed from core. ✤ jQuery.support replaces it.
  • 3.
    jQuery.support ✤ div.innerHTML = ' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a </a><select><option>text</option></select>';
  • 4.
    jQuery.support ✤ jQuery.support = { ! ! // IE strips leading whitespace when .innerHTML is used ! ! leadingWhitespace: div.firstChild.nodeType == 3, ! ! // Make sure that tbody elements aren't automatically inserted ! ! // IE will insert them into empty tables ! ! tbody: !div.getElementsByTagName("tbody").length, ! ! // Make sure that link elements get serialized correctly by innerHTML ! ! // This requires a wrapper element in IE ! ! htmlSerialize: !!div.getElementsByTagName("link").length, ! ! // Get the style information from getAttribute ! ! // (IE uses .cssText insted) ! ! style: /red/.test( a.getAttribute("style") ), ! ! // Make sure that URLs aren't manipulated ! ! // (IE normalizes it by default) ! ! hrefNormalized: a.getAttribute("href") === "/a", ! ! // Make sure that element opacity exists ! ! // (IE uses filter instead) ! ! opacity: a.style.opacity === "0.5", ! ! // Verify style float existence ! ! // (IE uses styleFloat instead of cssFloat) ! ! cssFloat: !!a.style.cssFloat, ! ! // Will be defined later ! ! scriptEval: false, ! ! noCloneEvent: true, ! ! boxModel: null ! };
  • 5.
    Modularity ✤ Starting in 1.3.3 code is broken up even more ✤ Core has been split into many sub-modules ✤ core.js ✤ attributes.js ✤ css.js ✤ manipulation.js ✤ traversing.js
  • 6.
    Modularity ✤ Starting to reduce dependencies in-between files ✤ Makes it easier to dynamically load portions of the library ✤ (Mobile jQuery!)
  • 7.
    Mobile jQuery? ✤ “Heavy” core.js ✤ ready event ✤ find() (powered by querySelectorAll) ✤ filter() (very basic functionality) ✤ $.getScript() (for loading modules) ✤ Still experimenting...
  • 8.
    Sizzle ✤ Standalone selector engine, landed in jQuery 1.3. Selector % Used # of Uses ✤ Built for selectors that people actually use. #id 51.290% 1431 .class 13.082% 365 tag 6.416% 179 tag.class 3.978% 111 #id tag 2.151% 60 tag#id 1.935% 54 #id:visible 1.577% 44 #id .class 1.434% 40 .class .class 1.183% 33 * 0.968% 27 #id tag.class 0.932% 26 #id:hidden 0.789% 22 tag[name=value] 0.645% 18 .class tag 0.573% 16 [name=value] 0.538% 15 tag tag 0.502% 14 #id #id 0.430% 12 #id tag tag 0.358% 10
  • 9.
    Right to Left ✤ Most selector engines (including jQuery, pre-1.3) evaluate a selector left to right ✤ “#id div” - finds the element by ID “id” then finds all divs inside of it. ✤ Sizzle finds all divs then checks to see if theres an ancestor with an id of “id”. ✤ How CSS engines work in browsers. ✤ Only one query per selector, rest is filtering.
  • 10.
  • 11.
    Analyzing Performance ✤ Optimizing performance is a huge concern: Faster code = happy users! ✤ Measure execution time ✤ Loop the code a few times ✤ Measure the difference: ✤ (new Date).getTime();
  • 12.
    Stack Profiling ✤ jQuery Stack Profiler ✤ Look for problematic methods and plugins ✤ http://ejohn.org/blog/deep-profiling-jquery-apps/
  • 14.
    FireUnit ✤ A simple JavaScript test suite embedded in Firebug. ✤ http://fireunit.org/
  • 15.
    FireUnit Profile Data { fireunit.getProfile(); "time": 8.443, "calls": 611, "data":[ { "name":"makeArray()", "calls":1, "percent":23.58, "ownTime":1.991, "time":1.991, "avgTime":1.991, "minTime":1.991, http://ejohn.org/blog/function-call- "maxTime":1.991, "fileName":"jquery.js (line 2059)" profiling/
  • 16.
    Complexity Analysis ✤ Analyze complexity rather than raw time ✤ jQuery Call Count Profiler (uses FireUnit) Method Calls Big-O .addClass("test"); 542 6n .addClass("test"); 592 6n .removeClass("test"); 754 8n .removeClass("test"); 610 6n .css("color", "red"); 495 5n .css({color: "red", border: "1px solid red"}); 887 9n .remove(); 23772 2n+n2 .append("<p>test</p>"); 307 3n
  • 17.
    Complexity Analysis ✤ Reducing call count helps to reduce complexity ✤ Results for 1.3.3: Method Calls Big-O .remove(); 298 3n .html("<p>test</p>"); 507 5n .empty(); 200 2n http://ejohn.org/blog/function-call-
  • 18.
    ECMAScript 5 Support ✤ New in 1.3.3 ✤ Removal of arguments.callee ✤ Switching from ‘RegExp to ‘new RegExp’ ✤ Using JSON.parse, where available. ✤ Support for json2.js is auto-baked in.
  • 19.
    Unified Syntax ✤ Reusable inline functions and RegExp moved outside. ✤ var rinlinejQuery = / jQueryd+="(?:d+|null)"/g, ! rleadingWhitespace = /^s+/, ! rsingleTag = /^<(w+)s*/?>$/, ! rxhtmlTag = /(<(w+)[^>]*?)/>/g, ! rselfClosing = /^(?:abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i, ! rinsideTable = /^<(thead|tbody|tfoot|colg|cap)/, ! rtbody = /<tbody/i, ! fcloseTag = function(all, front, tag){ ! ! return rselfClosing.test(tag) ? ! ! ! all : ! ! ! front + "></" + tag + ">"; ! }; ✤ http://docs.jquery.com/JQuery_Core_Style_Guidelines
  • 20.
    Type Checks String: typeof object === "string" • Number: typeof object === "number" • Boolean: typeof object === "boolean" • Object: typeof object === "object" • Function: jQuery.isFunction(object) • Array: jQuery.isArray(object) • Element: object.nodeType • null: object === null • undefined: typeof variable === "undefined" or object.prop === undefined • null or undefined: object == null
  • 21.
    isFunction / isArray ✤ isFunction overhauled in 1.3, isArray added in 1.3.3 ✤ toString = Object.prototype.toString isFunction: function( obj ) { ! ! return toString.call(obj) === "[object Function]"; ! }, ! isArray: function( obj ) { ! ! return toString.call(obj) === "[object Array]"; ! },
  • 22.
  • 23.
    .selector / .context ✤ Added in 1.3 ✤ Predominantly used for plugins ✤ var div = jQuery(“div”); div.selector === “div”; div.context === document; ✤ jQuery.fn.update = function(){ return this.pushStack( jQuery(this.selector, this.context), “update” ); };
  • 24.
    jQuery(...) unified ✤ In 1.3.3 ✤ jQuery(null), jQuery(false), jQuery(undefined) => jQuery([]) ✤ jQuery() is still the same as jQuery(document) (for now) ✤ BUT jQuery(“div”) points back to a common jQuery(document) root. ✤ jQuery(“div”).prevObject === jQuery()
  • 25.
    jQuery(“TAG”) ✤ In 1.3.3 ✤ jQuery(“body”) is now a shortcut for jQuery(document.body) ✤ jQuery(“TAG”) is optimized (skips Sizzle)
  • 26.
    Position ✤ All in 1.3.3 ✤ .get(-N), .eq(-N) Access elements/jQuery sets using negative indices. ✤ .first(), .last() Shortcuts for .eq(0) and .eq(-1) ✤ .toArray() Shortcut for .get()
  • 27.
    .data() ✤ In 1.3.3 ✤ Calling .data() (no args) returns the full data object for an element ✤ jQuery(“#foo”).data(“a”, 1).data(“b”, 2).data() => { a: 1, b: 2 }
  • 28.
  • 29.
    Document Order if ( document.documentElement.compareDocumentPosition ) { ! sortOrder = function( a, b ) { ! ! var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; ! ! if ( ret === 0 ) { ! ! ! hasDuplicate = true; ! ! } ! ! return ret; ! }; } else if ( "sourceIndex" in document.documentElement ) { ! sortOrder = function( a, b ) { ! ! var ret = a.sourceIndex - b.sourceIndex; ! ! if ( ret === 0 ) { ! ! ! hasDuplicate = true; ! ! } ! ! return ret; ! }; } else if ( document.createRange ) { ! sortOrder = function( a, b ) { ! ! var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); ! ! aRange.selectNode(a); ! ! aRange.collapse(true); ! ! bRange.selectNode(b); ! ! bRange.collapse(true); ! ! var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); ! ! if ( ret === 0 ) { ! ! ! hasDuplicate = true; ! ! } ! ! return ret; ! } }; ✤ As of 1.3.2 all selectors return in document order (as if you did getElementsByTagName(“*”))
  • 30.
    :hidden / :visible ✤ Changed in 1.3.2, revised in 1.3.3 ✤ Changed logic of :hidden/:visible to use .offsetWidth === 0 && .offsetHeight === 0 trick ✤ Sizzle.selectors.filters.hidden = function(elem){ ! var width = elem.offsetWidth, height = elem.offsetHeight, ! ! force = /^tr$/i.test( elem.nodeName ); // ticket #4512 ! return ( width === 0 && height === 0 && !force ) ? ! ! true : ! ! ! ( width !== 0 && height !== 0 && !force ) ? ! ! ! ! false : ! ! ! ! ! !!( jQuery.curCSS(elem, "display") === "none" ); };
  • 31.
    ID-rooted Queries ✤ Changed in 1.3.3 ✤ $(“#id div”) sped up dramatically ✤ #id is detected and used as the root of the query ✤ $(“div”, “#id”) --> $(“#id”).find(“div”) ✤ $(“#id div”) can likely never be as fast as $(“#id”).find(“div”) ✤ $(“#id”) is so hyper-optimized, it’s hard to match raw perf.
  • 32.
  • 33.
    HTML Fragments ✤ Overhauled in 1.3 ✤ .append(“<li>foo</li>”) first converts HTML to a DOM fragment ✤ Fragments act as a loose collection for DOM nodes ✤ Can be used to insert many nodes with a single operation ✤ var fragment = document.createDocumentFragment(); while ( node.firstChild ) fragment.appendChild( node.firstChild ); document.body.appendChild( node );
  • 34.
    HTML Fragments (redux.) ✤ In 1.3.3 ✤ It turns out that using fragments makes it really easy to cache them, as well. ✤ Look for common cases that can be cached (simple, small, HTML strings that are built more than once). ✤ Also optimized $(“<some html>”), cut out some unnecessary intermediary code. ✤ jQuery.fragments[“<some html>”] = fragment;
  • 35.
    HTML Fragments ✤ Surprising change: ✤ for ( var i = 0; i < 250; i++ ) { $(“<li>some thing</li>”).attr(“id”, “foo” + i).appendTo(“ul”); } ✤ Now faster than: ✤ for ( var i = 0; i < 250; i++ ) { $(“ul”).append(“<li id=‘foo” + i + “‘>some thing</li>”); }
  • 36.
    $(“<div/>”) ✤ In 1.3: ✤ $(“<div/>”) was made equivalent to $(document.createElement (“div”)) ✤ In 1.3.3: ✤ Logic for handling createElement moved to jQuery() (performance improved!)
  • 37.
    append(function(){ }) ✤ In 1.3.3 append, prepend, before, after, wrap, wrapAll, replace, replaceAll all take a function as an argument ✤ Compliments .attr(“title”, function(){}) (and CSS) ✤ Makes: $(“li”).each(function(){ $(this).append(“My id: “ + this.id); }); ✤ Also: $(“li”).append(function(){ return “My id: “ + this.id; });
  • 38.
    .detach() ✤ In 1.3.3 ✤ Like .remove() but leaves events and data intact. ✤ detach: function( selector ) { ! ! return this.remove( selector, true ); ! },
  • 39.
  • 40.
    .closest() ✤ Added in 1.3 ✤ $(this).closest(“div”) checks if ‘this’ is a div, if not keeps going up the tree until it finds the closest one. ✤ In 1.3.3 ✤ $(this).closest(“.test”, document.body) Prevent the traversal from going too far up the tree.
  • 41.
    find() perf ✤ In 1.3.3 ✤ Reduced the number of function calls - Reduced 16 calls per find() to 5. ✤ find: function( selector ) { ! ! var ret = this.pushStack( "", "find", selector ), length = 0; ! ! for ( var i = 0, l = this.length; i < l; i++ ) { ! ! ! length = ret.length; ! ! ! jQuery.find( selector, this[i], ret ); ! ! ! if ( i > 0 ) { ! ! ! ! // Make sure that the results are unique ! ! ! ! for ( var n = length; n < ret.length; n++ ) { ! ! ! ! ! for ( var r = 0; r < length; r++ ) { ! ! ! ! ! ! if ( ret[r] === ret[n] ) { ! ! ! ! ! ! ! ret.splice(n--, 1); ! ! ! ! ! ! ! break; ! ! ! ! ! ! } ! ! ! ! ! } ! ! ! ! } ! ! ! } ! ! } ! ! return ret; ! },
  • 42.
    find() perf ✤ Perf for $(“...”) improved, as well (hooked into rootQuery, uses less function calls)
  • 43.
    .not() / .filter() ✤ .not(function(){}) (1.3.3) ✤ .filter(Element), .filter(function(){}) (1.3.3) ✤ Full API parity inbetween .not() and .filter()
  • 44.
    .index() ✤ In 1.3.3 ✤ $(“div”).index() - position of element relative to siblings ✤ $(“#foo”).index(“div”) - position relative to all divs
  • 45.
  • 46.
    Live Events ✤ Super-efficient event delegation - uses .closest(), introduced in 1.3. ✤ 1.3.3 adds context and data object support. ✤ 1.3.3 will ship once “submit”, “change”, and “focus/blur” events work in .live() (in all browsers).
  • 47.
    .bind() `thisObject` ✤ In 1.3.3 ✤ You can now bind functions enforcing this `this` ✤ var obj = { method: function(){} }; $(“div”).bind( “click”, function(){ this.objProp = true; }, obj );
  • 48.
    Dynamic Ready Event ✤ In 1.3.3 ✤ document.readyState is checked to determine if the body is already loaded - if so, no need to wait for ready event.
  • 49.
    Method Calls O(N) .addClass("test"); 542 O(6n) .addClass("test"); 592 O(6n) .removeClass("test"); 754 O(8n) .removeClass("test"); 610 O(6n) .css("color", "red"); 495 O(5n) .css({color: "red", border: "1px solid red"}); 887 O(9n) .remove(); 23772 O(2n+n2) .append("<p>test</p>"); 307 O(3n) .append("<p>test</p><p>test</p><p>test</p><p>test</p><p>test</p>"); 319 O(3n) .show(); 394 O(4n) .hide(); 394 O(4n) .html("<p>test</p>"); 28759 O(3n+n2) .empty(); 28452 O(3n+n2) .is("div"); 110 .filter("div"); 216 O(2n) .find("div"); 1564 O(16n)
  • 50.
    Method Calls O(N) .addClass("test"); 2 .addClass("test"); 2 .removeClass("test"); 2 .removeClass("test"); 2 .css("color", "red"); 102 .css({color: "red", border: "1px solid red"}); 201 O(2n) .remove(); 299 O(3n) .append("<p>test</p>"); 116 .append("<p>test</p><p>test</p><p>test</p><p>test</p><p>test</p>"); 128 .show(); 394 O(4n) .hide(); 394 O(4n) .html("<p>test</p>"); 100 .empty(); 201 O(2n) .is("div"); 109 .filter("div"); 216 O(2n) .find("div"); 495 O(5n)