23.10.2014 Views

perf&compression

perf&compression

perf&compression

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

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


wassup shawty? how u doin’

Taskspeed Test Lines of Code

200

160

120

80

40

0

YUI

Dojo 1.3.1

Dojo 1.2.3

Qooxdoo

MooTools

Prototype.js

jQuery

PureDOM

Taskspeed Test Lines of Code

YUI

Dojo Qooxdoo MooTools Prototype.js

jQuery PureDOM 040

80 120 160 200

1.3.1 1.2.3


Oft cited best practices

Cache length during loops


// appending inside. bad.

$.each(reallyLongArray, function(count, item) {


});

Cache your selections

var newLI = '' + item + '';

$('#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 = '' + item + '';

frag.appendChild(newLI[0]);

});

$('#ballers')[0].appendChild(frag);


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

// requery to bind stuff

$("div.baaron").click(function(){});

// better:

// swap to appendTo to hold your elem

$("")

.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



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

div.data table.attendees .gonzalez


Left to right (Top-down)

Mootools

Right to left (Bottom-up)

Sizzle

Sly YUI 3

Peppy

Dojo Acme

NWMatcher

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

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

new in

1.4


Minimize DOM touches


Use classes, but if a style change user-selected:

jQuery('a.swedberg').css('color', '#BADA55');

jQuery(' a.swedberg { color: BADA55; } ')

.appendTo('head');

4000

3000

Timings for X elements

(1000 iterations)

2000

1000

css() style tag

css()

style tag

0

1

5

10

20

50

1510

50 01000

2000 3000 4000


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


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

in

1.4!

jQuery("", {

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

in

1.4!

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

var test1 = 1;

var test2 = function() {

// function code

};

var test3 = test2(test1);

// new hotness

var 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

(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.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-js\b/,'js');


// change the html class to 'js'

// one line ftw!

document.documentElement.className // in the head, no FOUC=

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


Conditionals

// old 'n busted

if ( type === 'foo' || type === 'bar' ) {}

// regex test

if ( /^(foo|bar)$/.test(type) ) {}

// obj literal lookup (smaller if


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/jquerydev/msg/b4b7935a4013dfe7

and

http://ispeakwebstuff.co.uk/web-design-developmenttutorials/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()

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!