jquery anti-patterns for performance & compression

Post on 29-Jan-2018

99.015 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

jQuery Anti-Patterns for Performance & CompressionPaul Irish

jQuery Conf ’09

Me.

Interaction Designer at Molecular, Inc.

@paul_irish

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

http://aurgasm.us Eclectic music blog

Performance

Performance

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!

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

Cache your selections

Leverage documentFragment

Append new content outside the loop

BORING

Keep things DRY

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

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

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{ $('#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(){ $('#yayeffects').slideUp(PI.slideCb); } slideCb : function(){ ... }, unicornCb : function(){ ... }}

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

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

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

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

$(‘#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');

$(‘#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?

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

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

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/

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/

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.

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

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

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

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

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

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)

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

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

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'

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

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;

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

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/,

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

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

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/

Case Study: Modernizr

Thanks, ya’ll.

Slides at http://paulirish.com/perf

@paul_irish

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

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

top related