• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
jQuery Anti-Patterns for Performance
 

jQuery Anti-Patterns for Performance

on

  • 1,640 views

jQuery Anti-Patterns for Performance

jQuery Anti-Patterns for Performance

Statistics

Views

Total Views
1,640
Views on SlideShare
1,640
Embed Views
0

Actions

Likes
3
Downloads
41
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

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
  • update the taskspeed shit.\ndelegation facts.\n
  • i hang in #jquery so a lot of examples are from real code discussed there.\n
  • \n
  • \n
  • \n
  • \n
  • like copypasting a line or three of code\n
  • \n
  • \n
  • \n
  • rebecca murphey will be discussing this technique a lot more\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • the convenience of context will incur the cost of three extra if() statements in jQuery.fn.init()\n
  • \n
  • selectors. ugh.\n
  • did it because i wanted to study.\nthe old ones are probablyw ayyyyy easier to study as the new ones use some crazy techniques\n
  • did it because i wanted to study.\nthe old ones are probablyw ayyyyy easier to study as the new ones use some crazy techniques\n
  • \n
  • before sizzle it was LTR. sizzle changed it.\n
  • before sizzle it was LTR. sizzle changed it.\n
  • before sizzle it was LTR. sizzle changed it.\n
  • be brief on the left\nthe more you can filter down the righthandmost expression, the faster it will run.\n
  • id is grabbed. optimization\n
  • in my testing it didnt speed up basic selecting.\n
  • \n
  • css engine too.\n
  • \n
  • TDs and LI’s etccc\n
  • \n
  • \n
  • \n
  • \n
  • document.body as an append target is WIN\n
  • \n
  • \n
  • padolsey’s research on animate()\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • strings take up a lot of space, so allowing them to be munged helps a lot\ncompress it and look for repetition\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • DRY obviously\n
  • DRY obviously\n
  • \n
  • really understand truthy and falsy ness\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

jQuery Anti-Patterns for Performance jQuery Anti-Patterns for Performance Presentation Transcript

  • jQuery Anti-Patterns forPerformance &CompressionPaul IrishNC JavaScript Camp ’10
  • jQuery Anti-Patterns forPerformance &CompressionPaul IrishNC JavaScript Camp ’10
  • Me.Interaction Designer at Molecular, Inc.jQuery Team Member - Dev. Relations @paul_irishhttp://paulirish.com Front-end development bloghttp://aurgasm.us Eclectic music blog
  • Performance
  • Performance
  • wassup shawty? how u doin’ Taskspeed Test Lines of Code20015010050 0 YUI Dojo 1.3.1 Dojo 1.2.3 Qooxdoo MooTools Prototype.js jQuery PureDOM
  • Oft cited best practices Cache length during loops Cache your selections Leverage documentFragment Append new content outside the loop
  • Oft cited best practices Cache length during loops// appending inside. bad.$.each(reallyLongArray, function(count, item) { Cache your selections var newLI = <li> + item + </li>; Leverage documentFragment $(#ballers).append(newLI);}); Append new content outside the loop// documentFragment off-DOMvar frag = document.createDocumentFragment();$.each(reallyLongArray, function(count, item) { var newLI = <li> + item + </li>; frag.appendChild(newLI[0]);});$(#ballers)[0].appendChild(frag);
  • var newLI = <li> + item + </li>; $(#ballers).append(newLI);});Oft cited best practices// documentFragment off-DOMvar frag = document.createDocumentFragment(); Cache length during loops$.each(reallyLongArray, function(count, item) { var newLI = <li> + item + </li>; Cache your selections frag.appendChild(newLI[0]);}); Leverage documentFragment$(#ballers)[0].appendChild(frag); Append new content outside the loop// string concatenate and set innerHTMLvar myhtml = ;$.each(reallyLongArray, function(count, item) { myhtml += <li> + item + </li>;});$(#ballers).html(myhtml);
  • Keep things DRY If you’re repeating yourself, you’re doing it wrong
  • Moar DRY plz?if ($ventfade.data(currently) != showing) { $ventfade.stop();}if ($venthover.data(currently) != showing) { $venthover.stop();}if ($spans.data(currently) != showing) { $spans.stop();} from http://mt-ventures.com/_js/global.js
  • All clean! Thxvar elems = [$ventfade,$venthover,$spans];$.each(elems,function(k,v){ if (v.data(currently) != showing){ v.stop(); }})
  • Architecture Anti-Patterns Anonymous functions bound everywhere suck$(document).ready(function(){ ... $(#magic).click(function(e){ $(#yayeffects).slideUp(function(){ ... }); }); $(#happiness).load(url+ #unicorns,function(){ ... })});
  • Architecture - Object Literalvar PI = { onReady : function(){ ... $(#magic).click(PI.candyMtn); $(#happiness).load(url+ #unicorns,PI.unicornCb); }, candyMtn : function(e){ $(#yayeffects).slideUp(PI.slideCb); }, slideCb : function(){ ... }, unicornCb : function(){ ... }}$(document).ready(PI.onReady);
  • Architecture - Object Literal Advantages: Easier to navigate and discuss Profilers give you actual names to work with You can execute these from firebug console You can write unit tests against them
  • Anti-Pattern: The requery// create and append your element$(document.body).append("<div class=baaron/>");// requery to bind stuff$("div.baaron").click(function(){});// better:// swap to appendTo to hold your elem$("<div class=baaron/>") .appendTo(document.body) .click(function(){});
  • $(‘#whats .the’,context)
  • This is not the .context property // find all stylesheets in the body var bodySheets = $(style,document.body); bodySheets.context // ==> BODY element Ignore that for the moment, I know no one that’s found a use
  • $(‘#whats .the’,context) Never pass it a selector string. Ever. No performance gain vs $(root).find(selector) var arms = $(div.robotarm, #container); // instead do: var arms = $(#container).find(div.robotarm);
  • $(‘#whats .the’,context) You typically pass it this, but it’s purely a convenience to avoid find() $(form.comments,this).submit(captureSubmit); // exact same as $(this).find(form.comments).submit(captureSubmit); Which is more readable?$(.reply_form, $(this).closest(.comment)).hide();$(this).closest(.comment).find(.reply_form).hide();
  • The Crowd Say Bo Selector
  • Come on, my selectorSelector engines have come a long, long way.
  • Come on, my selectorSelector engines have come a long, long way.
  • Come on, my selectorEngines work in different waysTop-down, bottom-up, function creation, other crazy shit// from NWMatcher:// selecting .outmost #outer spanT=e.nodeName;if(T=="SPAN"||T=="span"){while((e=e.parentNode)&&e.nodeType==1){if((n=e.getAttributeNode("id"))&&n.value=="outer"){if((e=e.parentNode)&&e.nodeType==1){C=e.className;if(C&&(" "+C+" ").indexOf(" outmost ")>-1){r[X++]=N;continue main;}}}}}
  • Selector engines, parse direction Left to right (Top-down) Right to left (Bottom-up) Mootools Sizzle Sly YUI 3 Peppy NWMatcher Dojo Acme Ext JS Prototype.js details: http://alexsexton.com/selectors/
  • Selector engines, parse direction div.data table.attendees .gonzalez Left to right (Top-down) Right to left (Bottom-up) Mootools Sizzle Sly YUI 3 Peppy NWMatcher Dojo Acme Ext JS Prototype.js details: http://alexsexton.com/selectors/
  • Selector engines, parse direction Left to right (Top-down) Right to left (Bottom-up) Mootools Sizzle Sly YUI 3 Peppy NWMatcher Dojo Acme Ext JS Prototype.js details: http://alexsexton.com/selectors/
  • Selector engines, parse direction Left to right (Top-down) Right to left (Bottom-up) Mootools Sizzle Sly YUI 3 Peppy NWMatcher Dojo Acme querySelectorAll (qSA) Ext JS Prototype.js details: http://alexsexton.com/selectors/
  • Selector Optimization Specific on the right, light on the left // lets find scott div.data .gonzalez // specific on right, light on the left .data td.gonzalez tag.class if possible on your right-most selector. just tag or just .class on left.
  • Selector Optimization Of course, descending from an #id is best// basic #id-based selectorvar arms = $(#container div.robotarm);// hyper-optimized #id case first, then find:var arms = $(#container).find(div.robotarm);
  • Selector Optimization Don’t be needlessly specific // lets find scott .data table.attendees td.gonzalez // better: drop the middle .data td.gonzalezA flatter DOM helps, so move to HTML5 Also a wider range of tags speeds up filters
  • Selector Optimization Avoid the universal selector Avoid the implied universal selector $(.buttons > *) // terribly costly $(.buttons).children() // much better $(.gender :radio) // implied universal $(.gender *:radio) // exact same, explicit now $(.gender input:radio) // much better
  • Selector Optimization Google PageSpeed’s efficient selectors analysis MDC: Writing Efficient CSS https://developer.mozilla.org/en/Writing_Efficient_CSS Benchmark.js http://code.paulirish.com/sandbox/benchmark.js
  • Event Delegation function delegate(type, delegate, handler) { return $(document).bind(type, function(event) { var target = $(event.target); if (target.is(delegate)) { return handler.apply(target, arguments); } }); } delegate(click,td.jehl,createRockstar); // and with live(): $(td.jehl).live(click,createRockstar);
  • Event Delegationlive() isn’t just for dynamic contentSpeeds up page load Only one event handler is bound vs manyGood for >3 elements all getting the same handler // using live(), skipping selection on load var jqElem = $(document); jqElem.selector = li.ui; jqElem.live(dblclick, dblhandler);
  • Event Delegationlive() isn’t just for dynamic contentSpeeds up page load Only one event handler is bound vs manyGood for >3 elements all getting the same handler // using live(), skipping selection on load var jqElem = $(document); jqElem.selector = li.ui; jqElem.live(dblclick, dblhandler);
  • Event Delegation delegate() bakes in huge performance gains explicit context reduces overhead by ~80% Use it instead of live() if possible// awkward but equivalent$(a.trigger,$(#container)[0]).live(click,handlerFn)// so damn fine$(#container).delegate(click,a.trigger,handlerFn)
  • Event Delegation new 1.4 in .2! delegate() bakes in huge performance gains explicit context reduces overhead by ~80% Use it instead of live() if possible// awkward but equivalent$(a.trigger,$(#container)[0]).live(click,handlerFn)// so damn fine$(#container).delegate(click,a.trigger,handlerFn)
  • The DOM is slowPull elements off the DOM while you toy with themvar table = $(#some-table);var parent = table.parent();table.detach();table.addLotsAndLotsOfRows();parent.append(table);
  • The DOM is slowPull elements off the DOM while you toy with themvar table = $(#some-table);var parent = table.parent(); newtable.detach(); in 1 .4table.addLotsAndLotsOfRows();parent.append(table);
  • Minimize DOM touches Use classes, but if a style change user-selected: jQuery(a.swedberg).css(color, #BADA55); jQuery(<style type="text/css"> a.swedberg { color: BADA55; } </style>) .appendTo(head);3000 Timings for X elements2250 (1000 iterations)1500 css() style tag750 0 1 5 10 20 50
  • Minimize DOM touches
  • Don’t treat jQuery as a Black Box Use the source as your documentation Add this to your bookmark bar, NOW! http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js http://bit.ly/jqsource Determine which are convenience methods: getScript: function( url, callback ) { return jQuery.get(url, null, callback, "script"); }, getJSON: function( url, data, callback ) { return jQuery.get(url, data, callback, "json"); },
  • Don’t treat jQuery as a Black Box Learn the lesser-known methods map(), slice(), stop(), (de)queue(), prevAll(), pushStack(), inArray() , etc // index() in jQuery <= 1.3.2 $(#rdworth).parent().children().index( $(#rdworth)[0] ) // using prevAll() is 10% faster (also sexier) $(#rdworth).prevAll().length // in jQuery 1.4 $(#rdworth).index()
  • Don’t act on absent elements jQuery is very kind and doesn’t throw errors at you Don’t assume it’s just fine to do $(#doesntexist).slideUp() // this will execute genFx(), speed() and animate() // before it hits an each() jQuery UI widgets have a lot of overhead you’ll hit
  • Don’t act on absent elements jQuery.fn.doOnce = function(func){ this.length && func.apply(this); return this; } $(li.cartitems).doOnce(function(){ // make it ajax! o/ });
  • Don’t act on absent elements $.fn.plugin = function(opts){ if(!this.length) return this; var opts = $.extend(...... ... return this.each(...
  • Setter Methodsview-source:setters.js
  • newNew Element Creation 1.4 ! injQuery("<div/>", { id: "foo", rel : "something" css: { height: "50px", width: "50px", color: "blue", backgroundColor: "#ccc" }, click: function() { $(this).css("backgroundColor", "red"); }}).appendTo("body");
  • neweq(), first(), last() 1.4 ! invar lastelem = $elems.eq(-1); // get() too!$(#nav li:first) === $(#nav li).first()$(#nav li:last) === $(#nav li).last()
  • Data()// regular:$(elem).data(key,value);// omg like 10x faster:$.data(elem,key,value);
  • Compression
  • CompressionYUI Compressor Sits on Rhino.Comments, whitespace, variable replacement//it already does these micro-optimizations:object[prop] ==> object.prop{key:123} ==> {key:123}jons apostophes ==> "jons apostrophes"bigass + string ==> bigass string
  • Variable definition// old n busted // new hotnessvar test1 = 1; var test1 = 1,var test2 = function() { test2 = function() { // function code // function code}; },var test3 = test2(test1); test3 = test2(test1);
  • Munge the primitivesDefine shortcuts at the top of your scope Good for both compression and scope chain traversalvar TRUE = true, FALSE = false, NULL = null, window = self, undefined = undefined;
  • Munge the primitivesDefine shortcuts at the top of your scope Good for both compression and scope chain traversalvar TRUE = true, FALSE = false, NULL = null, window = self, undefined; undefined = undefined;
  • Munge the primitives(function(){ var window = this, document = document,undefined; /* code */})();(function(window, document, undefined){ /* code */})(this,this.document);
  • var str=‘Let’s put this intoaction’ // html.no-js html> <!doctype ==> html.js var elem = document.getElementsByTagName(html)[0]; elem.className = elem.className.replace(no-js,js); <html class="no-js"> // quicker reference, safer replace <head> var elem = document.documentElement; elem.className = elem.className.replace(/bno-jsb/,js); <script> // one// change the html class to js line ftw! // in the head, no FOUC document.documentElement.className = document.documentElement.className.replace(/bno-jsb/, </script> js); </body> // shorter with a self-executing anonymous function (function(B){B.className=B.className.replace(/bno-jsb/,
  • var str=‘Let’s put this intoaction’ // html.no-js ==> html.js var elem = document.getElementsByTagName(html)[0]; elem.className = elem.className.replace(no-js,js); // quicker reference, safer replace var elem = document.documentElement; elem.className = elem.className.replace(/bno-jsb/,js); // one line ftw! document.documentElement.className = document.documentElement.className.replace(/bno-jsb/, js); // shorter with a self-executing anonymous function (function(B){B.className=B.className.replace(/bno-jsb/,
  • // html.no-js ==> html.jsvar str=‘Let’s put this intovar elem = document.getElementsByTagName(html)[0];elem.className = elem.className.replace(no-js,js);action’// quicker reference, safer replacevar elem = document.documentElement;elem.className = elem.className.replace(/bno-jsb/,js);// one line ftw!document.documentElement.className =document.documentElement.className.replace(/bno-jsb/,js);// shorter with a self-executing anonymous function(function(B){B.className=B.className.replace(/bno-jsb/,js)})(document.documentElement);// pass className, object string notation(function(H,C){H[C]=H[C].replace(/bno-jsb/,js)})(document.documentElement,className)
  • Conditionals// old n bustedif ( type === foo || type === bar ) {}// regex testif ( /^(foo|bar)$/.test(type) ) {}// obj literal lookup (smaller if <5 items)if ( ({foo:1,bar:1})[type] ) {}
  • Logic and Ternary operands// basic function detectiondocument.querySelectorAll && document.querySelectorAll(a:nth-child(2))// assignment is legal, but it evaluates to the right expressioncallback && (isCallbackCalled = true) && callback(returnVal);// call or cache the callback function(isCallbackCalled || returnVal) ? fn(returnVal) : (callback = fn);// inline function callsisToday(Saturday) && Math.round(Math.random()) && $(#winnar).show()// if JSON2.js or Native JSON is present, otherwise eval.data = window.JSON && JSON.parse(data) || eval((+data +));
  • Write maintainable code As a developer, you should work first and foremost for the user of your products. The second most important person to work for is the developer that takes over from you. - Christian Heilmann
  • Comments/*! * Will not be removed by YUI Compressor */// for quick toggling on and off:/* */ aaaahYeah();/* *//* * / ohHellNo();/* */
  • Compression ToolsCompressorRater http://compressorrater.thruhere.net/YUI Compressor front-end http://refresh-sf.com/yui/
  • Thanks, ya’ll. Slides at http://paulirish.com/perf @paul_irishthx: Alex Sexton, Ben Alman, Adam Sontag, James Padolsey, temp01, #jquery on Freenode
  • todoshadow effect to code samplesmore context research and this: http://groups.google.com/group/jquery-dev/msg/b4b7935a4013dfe7 and http://ispeakwebstuff.co.uk/web-design-development-tutorials/clever-jquery-selectors/
  • ` // pngfix for IE6 // e.g. FL.pngfix(img.bigProdShot,a.thumb); pngfix : function(sel){ // conditional comments for inclusion of that js. if (typeof DD_belatedPNG == undefined){ return; } else { // delay pngfix until window onload $(window).load(function(){ $(sel).each(function() { DD_belatedPNG.fixPng(arguments[1]); }); }); } } // end of FL.pngfix()