All You Can LeetA heaping helping of performance,
selector engines, & sandbox natives
By John-David Dalton
@jdalton ▪ [email protected] ▪ http://allyoucanleet.com
function times(iterator, context) { $R(0, this, true).each(iterator, context); return this;}
Performance
Reduce Abstraction
Performance
function times(iterator, context) { var i = -1, length = this; while (++i < length) iterator.call(context, i, i); return length;}
Reduce Abstraction
Performance
function contains(element, descendant) { if (element.compareDocumentPosition) { return (descendant .compareDocumentPosition(element) & 8) === 8; } if (element.contains) { return element !== descendant && element.contains(element); } while (descendant = descendant.parentNode) { if (descendant == element) return true; } return false;}
Fork like Rabbits
function isHostObject(object, property) { var type = typeof object[property]; return type === 'object' ? !!object[property] : !/^(boolean|number|string|undefined)$/.test(type);}var contains = function(element, descendant) { while (descendant = descendant.parentNode) if (descendant === element) return true; } return false;};
if (isHostObject(docEl, 'compareDocumentPosition')) { contains = function(element, descendant) { /* DOCUMENT_POSITION_CONTAINS = 0x08 */ return (descendant .compareDocumentPosition(element) & 8) === 8; };}else if (isHostObject(docEl, 'contains')) { contains = function(element, descendant) { return element !== descendant && element.contains(descendant); }
Performance
Fork like Rabbits
var type = typeof object[property]; return type === 'object' ? !!object[property] : !/^(boolean|number|string|undefined)$/.test(type);}
var contains = function(element, descendant) { while (descendant = descendant.parentNode) { if (descendant === element) return true; } return false;};if (isHostObject(docEl, 'compareDocumentPosition')) { contains = function(element, descendant) { /* DOCUMENT_POSITION_CONTAINS = 0x08 */ return (descendant .compareDocumentPosition(element) & 8) === 8; };}else if (isHostObject(docEl, 'contains')) { contains = function(element, descendant) { return element !== descendant && element.contains(descendant); };}
Performance
Fork like Robots
var contains = function(element, descendant) { while (descendant = descendant.parentNode) { if (descendant === element) return true; } return false;};
if (isHostObject(docEl, 'compareDocumentPosition')) { contains = function(element, descendant) { /* DOCUMENT_POSITION_CONTAINS = 0x08 */ return (descendant .compareDocumentPosition(element) & 8) === 8; };}else if (isHostObject(docEl, 'contains')) { contains = function(element, descendant) { return element !== descendant && element.contains(descendant); };}
Performance
Fork like Hobbits
var contains = function(element, descendant) { while (descendant = descendant.parentNode) { if (descendant === element) return true; } return false;};
if (isHostObject(docEl, 'compareDocumentPosition')) { contains = function(element, descendant) { /* DOCUMENT_POSITION_CONTAINS = 0x08 */ return (descendant .compareDocumentPosition(element) & 8) === 8; };}
else if (isHostObject(docEl, 'contains')) { contains = function(element, descendant) { return element !== descendant && element.contains(descendant); };}
But don't fork Marsellus Wallace !
Performance
var cache = { }, reHyphenated = /-([a-z])/gi, uid = +new Date;
function toUpperCase(match, letter) { return letter.toUpperCase();}
function camelCase(string) { var key = uid + string; return cache[key] || (cache[key] = string.replace(reHyphenated, toUpperCase));}
Memoize
Performance
Choose Your Engine
•base2
•DomAssistant
•IDQuery
•LlamaLab
•MyLibrary
•Sly
•uuQuery
•YASS
•YUI
•Acme (Dojo)
•DomQuery (ExtJS)
•NWMatcher (FuseJS, Prototype)
•Sizzle (jQuery, MochiKit, Prototype)
•Slick (MooTools)
Selector Engines
•base2
•DomAssistant
•IDQuery
•LlamaLab
•MyLibrary
•Sly
•uuQuery
•YASS
•YUI
•Acme (Dojo)
•DomQuery (ExtJS)
•NWMatcher (FuseJS, Prototype)
•Sizzle (jQuery, MochiKit, Prototype)
•Slick (MooTools)
Dare to Compare
Selector Engines
NWMatcher Chrome 1+ Firefox 1.5+ IE 6+ Opera 9.0+ Safari 2.0+
Legacy Chrome 1+ Firefox 1.5+ IE 6+ Opera 9.25+ Safari 2.0.4+
Sizzle Chrome 1+ Firefox 2.0+ IE 6+ Opera 9.0+ Safari 3.0+
Prototype 1.6.1+
* 1.6.0.3 supports Safari 2.0+
Compatible Source ▪ Compatible ▪ Possible Compatibility ▪ Failed Compatibility
Browser Support
Selector Engines
E E#fooId E.fooClass E F E > F E + F F ~ F F[foo] F[foo="bar"] E[foo^="bar"] E[foo$="bar"] E[foo*="bar"] E[foo~="bar"] E[foo|="en"] E:root E:empty E:nth-child(n) E:nth-of-type(n) E:nth-last-child(n)
NWMatcherSizzle Prototype 1.6.1+
E:nth-last-of-type(n) E:first-child E:last-child E:only-child E:first-of-type E:last-of-type E:only-of-type E:not(s) E:active E:focus E:enabled E:disabled E:checked E:link E:visited E:target E:lang(t)
E:nth-last-of-type(n) E:first-child E:last-child E:only-child E:first-of-type E:last-of-type E:only-of-type E:not(s) E:active E:focus E:enabled E:disabled E:checked E:link E:visited E:target E:lang(t)
E:nth-last-of-type(n) E:first-child E:last-child E:only-child E:first-of-type E:last-of-type E:only-of-type E:not(s) E:active E:focus E:enabled E:disabled E:checked E:link E:visited E:target E:lang(t)
Not Supported ▪ Supported ▪ Not Supported + Failed Compatibility
E E#fooId E.fooClass E F E > F E + F F ~ F F[foo] F[foo="bar"] E[foo^="bar"] E[foo$="bar"] E[foo*="bar"] E[foo~="bar"] E[foo|="en"] E:root E:empty E:nth-child(n) E:nth-of-type(n) E:nth-last-child(n)
E E#fooId E.fooClass E F E > F E + F F ~ F F[foo] F[foo="bar"] E[foo^="bar"] E[foo$="bar"] E[foo*="bar"] E[foo~="bar"] E[foo|="en"] E:root E:empty E:nth-child(n) E:nth-of-type(n) E:nth-last-child(n)
Legacy
Selector Engines
Selector Support
NWMatcherSizzle
QuerySelectorAll Bug Fixes
Prototype 1.6.1+
1. Quirks className case-sensitivity
2. Quirks mixed case className
3. :disabled :enabled on inputtype="hidden”
4. :link matching non-hyperlinks
5. :target support
6. Matching ^="" or $=""
7. Source attribute values
1. Quirks uppercase className
1. Quirks className case-insensitivity
Legacy
Selector Engines
CSS 3 Selector Tests: LegacySelector Engines
CSS 3 Selector Tests: SizzleSelector Engines
CSS 3 Selector Tests: NWMatcherSelector Engines
Match Maker[ event delegation; higher score is better ]
Selector Engines
Performance[ higher score is better ]
Selector Engines
Performance[ higher score is better ]
Selector Engines
Make the Switch
rake dist SELECTOR_ENGINE = nwmatcher
Selector Engines
The Problem
Array.prototype.size = function() { return this.length;};
var arr = [1, 2, 3];arr.size(); // 3
// Excluding the use of newer ES5 methods like// Object.defineProperty() when defining new properties// the internal [[DefineOwnProperty]] method is called with// a property descriptor containing [[Enumerable]] true.for (var i in arr) { // i is 0, 1, 2, `size`}
Sandbox Natives
The Problem
// framework X defines a methodFunction.prototype.defer = function() { var fn = this, args = arguments; return window.setTimeout(function() { fn.apply(fn, args); }, 10);};
// framework Y paves previous method with their ownFunction.prototype.defer = function(millis, thisArg, args) { var fn = this; return window.setTimeout(function() { fn.apply(thisArg, args); }, millis);};
Sandbox Natives
// framework X defines a methodArray.prototype.reduce = function() { return this.length > 1 ? this : this[0];};
// Later, a different implementation is added to ECMAScript// 15.4.4.21 Array#reduce ( callbackfn [ , initialValue ] )
// returns [1, 2, 3] instead of 6[1, 2, 3].reduce(function(prevValue, value) { return prevValue + value;});
The Problem
Sandbox Natives
// Sandbox natives to the rescuefuse.Array.prototype.size = function() { return fuse.Number(this.length);};
var arr = fuse.Array(1, 2, 3);arr.size(); // 3
// won't extend the global Array constructortypeof window.Array.prototype.size; // undefined
The Solution
Sandbox Natives
Browsers Tested
•IE 5.5+
•Firefox 1.5+
•Chrome 1+
•Konqueror 4.2.2+
•Opera 9.0+
•Safari 2.0+
Various JavaScript Engines•SpiderMonkey
•SquirrelFish (& Extreme)
•Tamarin
•TraceMonkey
•V8
•Carakan
•JaegerMonkey
•JavaScriptCore
•KJS
•Nitro
•Rhino
Sandbox Natives
Supported Natives
•fuse.Number
•fuse.Object
•fuse.RegExp
•fuse.String
•fuse.Array
•fuse.Boolean
•fuse.Date
•fuse.Function
Sandbox Natives
Usage
var str = fuse.String('bacon');
// sandbox natives cannot be primitivestypeof str; // object
// kinda like calling the global String constructortypeof new String('bacon'); // object
// internal [[Class]] is still [object String]({ }).toString.call(str);
// chaining works toostr.split('').join('').capitalize(); // Bacon
Sandbox Natives
What's Cool
// multiple sandboxed nativesfuse.Array;
fuse.dom.NodeList = (new fuse.Fusebox).Array;
// jQuery syntax with real arraysfuse.query('.tabs').addClassName('.active').show();
Sandbox Natives
FuseJS
Fuse JavaScript Framework
FuseJS
Browsers Tested
•IE 6+
•Firefox 1.5+
•Chrome 1+
•Konqueror 4.2.2
•Opera 9.25+
•Safari 2.0+
FuseJS
•Sandbox Native[ better 3rd party & browser support ]
•Performance[ Reduced abstraction & fork methods by browser features ]
•Zero Browser Sniffs[ feature detection, feature testing, object inference ]
•Framework Emulation[ PrototypeJS at beta, others later ]
•Debug Friendly[ console.log(fuse.Number(3).times); // times(callback, thisArg) ]
Features
FuseJS
•Modular[ less abstraction, custom builds ]
•Selector Engines[ Acme, DomQuery, NWMatcher, Peppy, Sizzle, Slick, Sly ]
•Quirks mode support[ dimensions, positioning ]
•DOM + Event decorators[ better 3rd party & browser support ]
•Cross iframe compatible[ fuse(iframeDoc).query('.widget') ]
Features
FuseJS
•Enforce FIFO Event Observer Order[ cross-browser and fixes edge cases ]
•Back-Forward Cache[ better performance between page navigation ]
•No Expandos Added in IE[ avoids unnecessary redraws ]
•Method Generics[ fuse.Array.slice(arr, 0) ]
•Separation of DOM Properties & Attributes[ fuse.dom.Element.plugin.getAttribute ]
Features
FuseJS
Performance[ lower score is better ]
FuseJS
Performance[ lower score is better ]
FuseJS
Performance[ IE8 - lower score is better ]
FuseJS
Performance[ lower score is better ]
Links
http://github.com/jdalton/fusejs
http://github.com/jdalton/fusebox
http://github.com/dperini/nwmatcher
http://fusejs.com
http://fusejs.com/nwmatcher/match/
http://fusejs.com/prototypejs/speed/?css=yahoo
http://javascript.nwbox.com/NWMatcher/release/test/css3-compat/
Twitter: @jdalton @fusejs
Email: [email protected]