Recent Changes to jQuery's Internals
Upcoming SlideShare
Loading in...5
×
 

Recent Changes to jQuery's Internals

on

  • 25,771 views

A talk I gave at the 2009 jQuery Conference.

A talk I gave at the 2009 jQuery Conference.

Statistics

Views

Total Views
25,771
Views on SlideShare
19,663
Embed Views
6,108

Actions

Likes
34
Downloads
526
Comments
2

45 Embeds 6,108

http://ejohn.org 5085
http://www.myphpetc.com 256
http://blog.arnaud-k.fr 174
http://www.juliobitencourt.com 120
http://10.36.244.82 99
http://feeds.feedburner.com 89
http://www.proyecto-f.net 63
http://xss.yandex.net 46
http://www.slideshare.net 24
http://www.denisdeng.com 24
http://www.waebo.com 17
http://www.zhuaxia.com 12
http://xianguo.com 12
http://greenido.wordpress.com 11
http://www.tichou.eu 10
http://blog.youmila.com 10
http://onwebdev.blogspot.com 7
http://127.0.0.1 6
http://www.netvibes.com 4
http://www.iweb34.com 4
http://lanyrd.com 3
http://reader.youdao.com 3
http://s.deeeki.com 3
http://webcache.googleusercontent.com 2
http://www.journogeekery.com 2
http://translate.googleusercontent.com 2
http://static.slidesharecdn.com 2
http://zhuaxia.com 1
http://dashboard.bloglines.com 1
http://rajesh4usability.blogspot.tw 1
http://tweetree.com 1
http://rajesh4usability.blogspot.com 1
http://blog.gabrieleromanato.com 1
http://rajesh4usability.blogspot.in 1
http://rss.knah-tsaeb.org 1
resource://brief-content 1
http://www.hanrss.com 1
http://www.brijj.com 1
http://127.0.0.1:8795 1
http://209.85.229.132 1
http://feeds2.feedburner.com 1
http://66.249.89.132 1
http://femmebot.tumblr.com 1
file:// 1
http://vizzwebsolutions.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Recent Changes to jQuery's Internals Recent Changes to jQuery's Internals Presentation Transcript

  • 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>'; View slide
  • 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 ! }; View slide
  • 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)