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

jQuery Anti-Patterns for Performance & Compression

on

  • 98,039 views

 

Statistics

Views

Total Views
98,039
Views on SlideShare
58,794
Embed Views
39,245

Actions

Likes
184
Downloads
2,110
Comments
10

82 Embeds 39,245

http://paulirish.com 28760
http://www.paulirish.com 7643
http://www.dustinmartin.net 734
http://richchetwynd.com 331
http://www.dennsia.com 252
http://www.slideshare.net 246
http://www.iamntz.com 225
http://inifinity.tumblr.com 184
http://jasonwyatt.tumblr.com 76
http://0.0.0.0 70
http://thefronts.com 69
http://dustinmartin.net 60
http://www.tangentlabs.mx 60
http://www.najamsikander.com 47
http://mezzin.wordpress.com 37
http://speakerrate.com 35
http://tangentlabs.dev 34
http://us-w1.rockmelt.com 31
http://www.scoop.it 28
http://www.dennsia.es 20
http://portal.accelrys.net 20
http://localhost 17
http://www.twylah.com 16
http://elgreg.org 16
http://www.denisdeng.com 15
http://paper.li 15
http://onwebdev.blogspot.com 13
http://deanyan.com 13
http://solucionesdigital.dev 10
http://webcache.googleusercontent.com 10
http://sebastienhiticas.com 10
http://translate.googleusercontent.com 8
http://www.journogeekery.com 8
http://static.slidesharecdn.com 8
http://es.freqly.com 8
http://najamsk.github.io 7
http://tugll.tugraz.at 7
https://twimg0-a.akamaihd.net 6
http://seenthis.net 6
http://www.netvibes.com 6
https://twitter.com 5
http://najamsikander.com 5
http://xss.yandex.net 5
http://192.81.214.198 4
http://solucionesdigital.com 4
http://blog.najamsikander.com 4
http://web-proxy02.appspot.com 4
http://cache.baidu.com 4
http://www.inifinity.tumblr.com 3
http://itblog.unister.lan 3
More...

Accessibility

Upload Details

Uploaded via as Apple Keynote

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

110 of 10 previous next Post a comment

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

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

  • jQuery Anti-Patterns for Performance & Compression Paul Irish NC JavaScript Camp ’10
  • jQuery Anti-Patterns for Performance & Compression Paul Irish NC JavaScript Camp ’10
  • Me. Interaction Designer at Molecular, Inc. jQuery Team Member - Dev. Relations @paul_irish http://paulirish.com Front-end development blog http://aurgasm.us Eclectic music blog
  • Performance
  • Performance
  • wassup shawty? how u doin’ Taskspeed Test Lines of Code 200 150 100 50 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>'; $('#ballers').append(newLI); Leverage documentFragment }); Append new content outside the loop // documentFragment off-DOM var 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-DOM var 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 innerHTML var 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! Thx var 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 Literal var 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 selector Selector engines have come a long, long way.
  • Come on, my selector Selector engines have come a long, long way.
  • Come on, my selector Engines work in different ways Top-down, bottom-up, function creation, other crazy shit // from NWMatcher: // selecting '.outmost #outer span' T=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 // let's 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 selector var arms = $('#container div.robotarm'); // hyper-optimized #id case first, then find: var arms = $('#container').find('div.robotarm');
  • Selector Optimization Don’t be needlessly specific // let's find scott .data table.attendees td.gonzalez // better: drop the middle .data td.gonzalez A 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 Delegation live() isn’t just for dynamic content Speeds up page load Only one event handler is bound vs many Good 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 live() isn’t just for dynamic content Speeds up page load Only one event handler is bound vs many Good 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 in 1.4 .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 slow Pull elements off the DOM while you toy with them var table = $('#some-table'); var parent = table.parent(); table.detach(); table.addLotsAndLotsOfRows(); parent.append(table);
  • The DOM is slow Pull elements off the DOM while you toy with them var table = $('#some-table'); var parent = table.parent(); new table.detach(); in 1 .4 table.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'); Timings for X elements 3000 2250 (1000 iterations) 1500 css() style tag 750 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 Methods view-source:setters.js
  • new New Element Creation 1.4 ! in jQuery("<div/>", { id: "foo", rel : "something" css: { height: "50px", width: "50px", color: "blue", backgroundColor: "#ccc" }, click: function() { $(this).css("backgroundColor", "red"); } }).appendTo("body");
  • new eq(), first(), last() 1.4 ! in var 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
  • Compression YUI Compressor Sits on Rhino. Comments, whitespace, variable replacement //it already does these micro-optimizations: object['prop'] ==> object.prop {'key':123} ==> {key:123} 'jon's apostophes' ==> "jon's apostrophes" 'bigass ' + 'string' ==> 'bigass string'
  • Variable definition // old 'n busted // new hotness var test1 = 1; var test1 = 1, var test2 = function() { test2 = function() { // function code // function code }; }, var test3 = test2(test1); test3 = test2(test1);
  • Munge the primitives Define shortcuts at the top of your scope Good for both compression and scope chain traversal var TRUE = true, FALSE = false, NULL = null, window = self, undefined = undefined;
  • Munge the primitives Define shortcuts at the top of your scope Good for both compression and scope chain traversal var 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 into action’ // 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 into action’ // 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.js var elem = document.getElementsByTagName('html')[0]; var str=‘Let’s put this into action’ 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/, '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 busted if ( type === 'foo' || type === 'bar' ) {} // regex test if ( /^(foo|bar)$/.test(type) ) {} // obj literal lookup (smaller if <5 items) if ( ({foo:1,bar:1})[type] ) {}
  • Logic and Ternary operands // basic function detection document.querySelectorAll && document.querySelectorAll('a:nth-child(2)') // assignment is legal, but it evaluates to the right expression callback && (isCallbackCalled = true) && callback(returnVal); // call or cache the callback function (isCallbackCalled || returnVal) ? fn(returnVal) : (callback = fn); // inline function calls isToday('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 Tools CompressorRater http://compressorrater.thruhere.net/ YUI Compressor front-end http://refresh-sf.com/yui/
  • Thanks, ya’ll. Slides at http://paulirish.com/perf @paul_irish thx: Alex Sexton, Ben Alman, Adam Sontag, James Padolsey, temp01, #jquery on Freenode
  • todo shadow effect to code samples more 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()