jquery anti-patterns for performance & compression

57
jQuery Anti-Patterns for Performance & Compression Paul Irish jQuery Conf ’09

Upload: paul-irish

Post on 29-Jan-2018

99.015 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: jQuery Anti-Patterns for Performance & Compression

jQuery Anti-Patterns for Performance & CompressionPaul Irish

jQuery Conf ’09

Page 2: jQuery Anti-Patterns for Performance & Compression

Me.

Interaction Designer at Molecular, Inc.

@paul_irish

http://paulirish.com Front-end development blog

http://aurgasm.us Eclectic music blog

Page 3: jQuery Anti-Patterns for Performance & Compression

Performance

Page 4: jQuery Anti-Patterns for Performance & Compression

Performance

Page 5: jQuery Anti-Patterns for Performance & Compression

0

50

100

150

200

YUI Dojo 1.3.1 Dojo 1.2.3 Qooxdoo MooTools Prototype.js jQuery PureDOM

Taskspeed Test Lines of Code

... but, jQuery is so sexy!

Page 6: jQuery Anti-Patterns for Performance & Compression

Oft cited best practices

Cache length during loops

Cache your selections

Leverage documentFragment

Append new content outside the loop

Page 7: jQuery Anti-Patterns for Performance & Compression

Oft cited best practices

Cache length during loops

Cache your selections

Leverage documentFragment

Append new content outside the loop

BORING

Page 8: jQuery Anti-Patterns for Performance & Compression

Keep things DRY

If you’re repeating yourself, you’re doing it wrong

Page 9: jQuery Anti-Patterns for Performance & Compression

Moar DRY plz?

from http://mt-ventures.com/_js/global.js

if ($ventfade.data('currently') != 'showing') { $ventfade.stop();}if ($venthover.data('currently') != 'showing') { $venthover.stop();}if ($spans.data('currently') != 'showing') { $spans.stop();}

Page 10: jQuery Anti-Patterns for Performance & Compression

All clean! Thx

var elems = [$ventfade,$venthover,$spans];

$.each(elems,function(k,v){ if (v.data('currently') != 'showing'){ v.stop(); }})

Page 11: jQuery Anti-Patterns for Performance & Compression

Architecture Anti-Patterns

Anonymous functions bound everywhere suck

$(document).ready(function(){ ... $('#magic').click(function{ $('#yayeffects').slideUp(function(){ ... }); }); $('#happiness').load(url+' #unicorns',function(){ ... }) });

Page 12: jQuery Anti-Patterns for Performance & Compression

Architecture - Object Literalvar PI = { onReady : function(){ ... $('#magic').click(PI.candyMtn); $('#happiness').load(url+' #unicorns',PI.unicornCb); } candyMtn : function(){ $('#yayeffects').slideUp(PI.slideCb); } slideCb : function(){ ... }, unicornCb : function(){ ... }}

$(document).ready(PI.onReady);

Page 13: jQuery Anti-Patterns for Performance & Compression

Architecture - Object Literal

Advantages:

Profilers give you actual names to work with

You can execute these from firebug console

You can write unit tests against them

Page 14: jQuery Anti-Patterns for Performance & Compression

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(){});

Page 15: jQuery Anti-Patterns for Performance & Compression

$(‘#whats .the’,context)

Page 16: jQuery Anti-Patterns for Performance & Compression

This is not the .context property

Ignore that for the moment, I know no one that’s found a use

// find all stylesheets in the bodyvar bodySheets = $('style',document.body);bodySheets.context // ==> BODY element

Page 17: jQuery Anti-Patterns for Performance & Compression

$(‘#whats .the’,context)

Never pass it a string. Ever.

No performance gain vs $(root).find(selector)

var arms = $('div.robotarm', '#container');// insteadvar arms = $('#container').find('div.robotarm');

Page 18: jQuery Anti-Patterns for Performance & Compression

$(‘#whats .the’,context)

You typically pass it this, but it’s purely a convenience to avoid find()

$('.reply_form', $(this).closest('.comment')).hide();

$(this).closest('.comment').find('.reply_form').hide();

$('form.comments',this).submit(captureSubmit);// exact same as$(this).find('form.comments').submit(captureSubmit);

Which is more readable?

Page 19: jQuery Anti-Patterns for Performance & Compression

The Crowd Say Bo Selector

Page 20: jQuery Anti-Patterns for Performance & Compression

Come on, my selector

Selector engines have come a long, long way.

Page 21: jQuery Anti-Patterns for Performance & Compression

Come on, my selector

Selector engines have come a long, long way.

Page 22: jQuery Anti-Patterns for Performance & Compression

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;}}}}}

Page 23: jQuery Anti-Patterns for Performance & Compression

Selector engines, parse directionLeft 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/

Page 24: jQuery Anti-Patterns for Performance & Compression

Selector engines, parse directionLeft 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/

div.data table.attendees .gonzalez

Page 25: jQuery Anti-Patterns for Performance & Compression

Selector engines, parse directionLeft 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/

Page 26: jQuery Anti-Patterns for Performance & Compression

Selector engines, parse directionLeft to right (Top-down) Right to left (Bottom-up)

Mootools Sizzle

Sly YUI 3

Peppy NWMatcher

Dojo Acme

Ext JS

Prototype.js

querySelectorAll (qSA)

details: http://alexsexton.com/selectors/

Page 27: jQuery Anti-Patterns for Performance & Compression

Selector Optimization

Specific on the right, light on the left

// let's find scottdiv.data table.attendees .gonzalez // better: drop the middlediv.data .gonzalez// best, light on the left.data td.gonzalez

tag.class if possible on your right-most selector. just tag or just .class on left.

Page 28: jQuery Anti-Patterns for Performance & Compression

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');

Page 29: jQuery Anti-Patterns for Performance & Compression

Selector Optimization

Don’t be needlessly specific

// let's find scottdiv.data table.attendees .gonzalez

// better: drop the middlediv.data .gonzalez

A flatter DOM helps, so move to HTML5

Also a wider range of tags speeds up filters

Page 30: jQuery Anti-Patterns for Performance & Compression

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

Page 31: jQuery Anti-Patterns for Performance & Compression

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

Page 32: jQuery Anti-Patterns for Performance & Compression

Event delegation

function delegate(type, delegate, handler) { return this.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);

Page 33: jQuery Anti-Patterns for Performance & Compression

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

// speed up live on page loadvar jqElem = $(document); jqElem.selector = 'li.ui';jqElem.live('dblclick', dblhandler);

Page 34: jQuery Anti-Patterns for Performance & Compression

The DOM is slow

Pull elements off the DOM while you toy with them

var table = $('#some-table'); var parent = table.parent(); table.remove(); table.addLotsAndLotsOfRows(); parent.append(table);

Page 35: jQuery Anti-Patterns for Performance & Compression

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');

0

750

1500

2250

3000

1 5 10 20 50

css()style tag

Timings for X elements

(1000 iterations)

Page 37: jQuery Anti-Patterns for Performance & Compression

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");},

Page 38: jQuery Anti-Patterns for Performance & Compression

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.3.3$('#rdworth').index()

Page 39: jQuery Anti-Patterns for Performance & Compression

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

Page 40: jQuery Anti-Patterns for Performance & Compression

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/});

Page 41: jQuery Anti-Patterns for Performance & Compression

Don’t act on absent elements

$.fn.plugin = function(opts){ if(!this.length) return this; var opts = $.extend(...... ... return this.each(...

Page 42: jQuery Anti-Patterns for Performance & Compression

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

Page 43: jQuery Anti-Patterns for Performance & Compression

Compression

Page 44: jQuery Anti-Patterns for Performance & Compression

Compression

YUI Compressor

Sits on Rhino. Kind of like an oxpecker.

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'

Page 45: jQuery Anti-Patterns for Performance & Compression

Variable definition

// old 'n bustedvar test1 = 1;var test2 = function() { // function code};var test3 = test2(test1);

// new hotnessvar test1 = 1, test2 = function() { // function code }, test3 = test2(test1);

Page 46: jQuery Anti-Patterns for Performance & Compression

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;

Page 47: jQuery Anti-Patterns for Performance & Compression

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;

var TRUE = true, FALSE = false, NULL = null, window = self, undefined;

Page 48: jQuery Anti-Patterns for Performance & Compression

var str=‘Let\’s put this into action’

// if we've got javascript, we'll switch the classvar elem = document.getElementsByTagName('html');elem.className = elem.className.replace('no-js','js');

// quicker reference, safer replacevar elem = document.documentElement;elem.className = elem.className.replace(/\bno-js\b/,'js');

// one line ftw!document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/, 'js');

// shorter with a self-executing anonymous function(function(B){B.className=B.className.replace(/\bno-js\b/,

Page 49: jQuery Anti-Patterns for Performance & Compression

var str=‘Let\’s put this into action’

// if we've got javascript, we'll switch the classvar elem = document.getElementsByTagName('html');elem.className = elem.className.replace('no-js','js');

// quicker reference, safer replacevar elem = document.documentElement;elem.className = elem.className.replace(/\bno-js\b/,'js');

// one line ftw!document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/, 'js');

// shorter with a self-executing anonymous function(function(B){B.className=B.className.replace(/\bno-js\b/, 'js')})(document.documentElement);

// pass className, object string notation(function(H,C){H[C]=H[C].replace(/\bno-js\b/,'js')})(document.documentElement,'className')

Page 50: jQuery Anti-Patterns for Performance & Compression

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] ) {}

Page 51: jQuery Anti-Patterns for Performance & Compression

Logic and Ternary operands

// basic function detectiondocument.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 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 +')');

Page 52: jQuery Anti-Patterns for Performance & Compression

Comments

/*! * Will not be removed by YUI Compressor */

// for quick toggling on and off:/* */ aaaahYeah();/* */

/* * / ohHellNo();/* */

Page 53: jQuery Anti-Patterns for Performance & Compression

Compression Tools

CompressorRater

http://compressorrater.thruhere.net/

YUI Compressor front-end

http://refresh-sf.com/yui/

Page 54: jQuery Anti-Patterns for Performance & Compression

Case Study: Modernizr

Page 55: jQuery Anti-Patterns for Performance & Compression

Thanks, ya’ll.

Slides at http://paulirish.com/perf

@paul_irish

thx:Alex Sexton, Ben Alman, Adam Sontag, James Padolsey, #jquery on Freenode

Page 57: jQuery Anti-Patterns for Performance & Compression

// 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()