Slide 1

Slide 1 text

jQuery Proven TIPS & TRICKS WITH ADDY OSMANI IMAGES COPYRIGHT HASBRO AND TONKA, 1935-2011. PERFORMANCE and Mr. Monopoly

Slide 2

Slide 2 text

ABOUT ME •JavaScript & UI Developer at Aol • jQuery Core [Bugs/Docs/Learning] teams • SocketStream Core Team Member • Writer [Script Junkie / AddyOsmani.com/.net etc]

Slide 3

Slide 3 text

We used to give out these awesome free coasters back in the 90s.

Slide 4

Slide 4 text

We now create real-time web frameworks and next-gen platforms.

Slide 5

Slide 5 text

Enough of that.. LET’S START!

Slide 6

Slide 6 text

WHY DOES PERFORMANCE MATTER? •Apps should be snappy, not sloppy. •Best practices offer optimal approaches to solving problems. •If we don’t follow them, browsers can end up having to do more work.

Slide 7

Slide 7 text

MORE WORK = MORE MEMORY USE = SLOWER APPS.

Slide 8

Slide 8 text

TODAY, ALL OF THESE SLIDES COME WITH PERFORMANCE TESTS. Not just saying X is faster...we’re proving it too.

Slide 9

Slide 9 text

PERFORMANCE TESTING • jsPerf.com - a great way to easily create tests comparing the performance of code snippets across different browsers • Makes it simple for anyone to share or modify tests • Used by the jQuery project, Yahoo and many other dev. teams Thanks to Mathias Bynens for creating it!

Slide 10

Slide 10 text

Example of test output http://jsperf.com/jquery-tree-traversing Anyone can tell what the fastest and slowest snippets are.

Slide 11

Slide 11 text

Quick jsPerf tips for beginners • ops/sec is the number of times a test is projected to execute in a second • Tests get repeatedly executed until they reach the minimum time required to get a percentage uncertainly • Results based on ops/sec accounting for margin of error. The higher ops/sec the better.

Slide 12

Slide 12 text

1 PERFORMANCE TIP

Slide 13

Slide 13 text

STAY UP TO DATE! •Always use the latest version of jQuery core where possible. •Remember to regression test your scripts and plugins before upgrading. •Current version is 1.6.2 and 1.7 will probably get released this fall.

Slide 14

Slide 14 text

MOST POPULAR SITES USING JQUERY ON THE GOOGLE CDN Stats from Scott Mitchell Old

Slide 15

Slide 15 text

INTERESTING FACTS •Performance improvements and new features usually land in major releases (eg. 1.6/1.x) •Bug patches and regression fixes land in 1.x.y releases (eg. 1.6.2) •Plenty of reasons to upgrade!

Slide 16

Slide 16 text

WHY? •Older versions won’t offer these instant performance benefits •As 47% of the popular sites on the web use jQuery, changes are heavily tested. •Upgrading usually a pain-free process.

Slide 17

Slide 17 text

Selector comparisons1.4.2 vs. 1.4.4 vs. 1.6.2 $(’.elem’) $(’.elem’, context); context.find(’.elem’); 0 27500 55000 82500 110000 1.4.2 1.4.4 1.6.2 http://jsperf.com/jquery-1-4-2-vs-1-6-2-comparisons

Slide 18

Slide 18 text

1.6.x improvements .attr() performance improved http://jsperf.com/attr-vs-attrhooks http://jsperf.com/valhooks-vs-val/2 .val() faster in 1.6.x

Slide 19

Slide 19 text

Note •There are certain selectors that are slower in 1.6.x than they are in 1.4.x •Be aware of the performance of selectors you’re using and you’ll be fine

Slide 20

Slide 20 text

2 PERFORMANCE TIP

Slide 21

Slide 21 text

KNOW YOUR SELECTORS •All selectors are not created equally •Just because a selection can be made in many ways, doesn’t mean each selector is just as performant •Do you know what the fastest to slowest selectors are?

Slide 22

Slide 22 text

Fast: ID & Element Selectors •ID and element selectors are the fastest •This is because they’re backed by native DOM operations (eg. getElementById()). $(‘#Element,  form,  input’)

Slide 23

Slide 23 text

Slower: Class Selectors •getElementsByClassName() not supported in IE5-8 •Supported in FF3+, Safari 4+, Chrome 4+, Opera 10.10+ so faster in these. $(‘.element’) http://www.quirksmode.org/dom/w3c_core.html

Slide 24

Slide 24 text

Slowest: Pseudo & Attribute Selectors • This is due to no native calls available that we can take advantage of. • querySelector() and querySelectorAll() help with this in modern browsers. http://www.quirksmode.org/dom/w3c_core.html $(‘:visible,  :hidden’);   $(‘[attribute=value]’);

Slide 25

Slide 25 text

querySelectorAll() • Allows searching the DOM for elems based on a CSS selector in modern browsers. • jQuery attempts to use qSA without hitting Sizzle for queries including $(‘#parent .child’) or $(‘.parent a[href!=”hello”]’) • Optimise for selectors that use qSA vs. those that don’t such as :first, :last, :eq etc. • Valid selectors have a better chance of using it.

Slide 26

Slide 26 text

jsPerf selector comparison ID Class Descendent tag Attributes Input/form select :nth-child 0 75000 150000 225000 300000 1.4.2 1.6.2 http://jsperf.com/dh-jquery-1-4-vs-1-6/6

Slide 27

Slide 27 text

Pseudo-selectors are powerful..but slow, so be careful when using them. BUT I’M TOO PRETTY TO GO TO JAIL!

Slide 28

Slide 28 text

The :hidden pseudo-selector • Looking at the code, why is this bad? if ( jQuery.expr && jQuery.expr.filters ) { jQuery.expr.filters.hidden = function( elem ) { var width = elem.offsetWidth, height = elem.offsetHeight; return (width === 0 && height === 0) ||(!jQuery.support.reliableHiddenOffsets && (elem.style.display ||jQuery.css( elem, "display" )) === "none"); }; jQuery.expr.filters.visible = function( elem ) { return !jQuery.expr.filters.hidden( elem ); }; }

Slide 29

Slide 29 text

Be careful because.. •If you use this with 100 elements, jQuery calls it 100 times. •:hidden is powerful but like all pseudos must be run against all the elements in your search space. •If possible, avoid them!.

Slide 30

Slide 30 text

jsPerf performance tests • jQuery1.4.2 vs 1.6 selector comparison tests http://jsperf.com/dh-jquery-1-4-vs-1-6/6 • jQuery 1.2.x vs 1.4.x vs. 1.6.x vs. qSA vs. qS vs. other frameworks http://jsperf.com/jquery-vs- sizzle-vs-midori-vs-mootools-selectors-test/26

Slide 31

Slide 31 text

3 PERFORMANCE TIP

Slide 32

Slide 32 text

UNDERSTAND PARENTS AND CHILDREN 1) $(‘.child", $parent).show(); //context 2) $parent.find(‘.child’).show(); //find() 3) $parent.children(".child’).show(); //immediate children 4) $(‘#parent > .child’).show(); //child combinator selector 5) $(‘#parent .child’).show(); //class selector 6) $('.child', $('#parent')).show(); //created context

Slide 33

Slide 33 text

Context •Here the scope must be parsed and translated to $.parent.find(‘child’).show(); causing it to be slower. •~5-10% slower than the fastest option 1)  $(‘.child’,  $parent).show();  

Slide 34

Slide 34 text

.find() •This is the fastest of the entire set. I’ll explain why shortly. 2)  $parent.find(‘.child’).show();  

Slide 35

Slide 35 text

Immediate children •Internally uses $.sibling and JavaScript’s nextSibling() to find nodes following other nodes in the same tree. •~50% slower than the fastest option 3)  $parent.children(‘.child’).show();  

Slide 36

Slide 36 text

CSS child combinator selector •Uses a child combinator selector, however Sizzle works from right to left. •Bad as it will match .child before checking it’s a direct child of the parent. •~70% slower than the fastest option 4)  $(‘#parent  >  .child’).show();

Slide 37

Slide 37 text

CSS class selector • Uses a class selector and is constrained by the same rules as 4). • Internally also has to translate to using .find() • ~77% slower than the fastest option 5)  $(‘#parent  .child’).show();  

Slide 38

Slide 38 text

Created context • Equivalent internally to $(‘#parent’).find(‘.child’), however note that parent is a jQuery object. • ~23% slower than the fastest option 6)  $(‘.child’,  $(‘#parent’)).show();  

Slide 39

Slide 39 text

The fastest option is.. • The parent selector is already cached here, so it doesn’t need to be refetched from the DOM. • Without caching this is ~ 16% slower. • Directly uses native getElementById, getElementsByName, getElementsByTagName to search inside the passed context under the hood. 2)  $parent.find(‘.child’).show();  

Slide 40

Slide 40 text

It’s worth noting.. •.find() performs a recursive top-down search of all child and sub-elements •Other options presented may be more suitable/performant depending on what you’re trying to achieve.

Slide 41

Slide 41 text

jsPerf performance tests •context vs. selector vs. selector and .find() vs. parent/child selector vs. immediate children: http://jsperf.com/jquery- selectors-context/2

Slide 42

Slide 42 text

4 PERFORMANCE TIP

Slide 43

Slide 43 text

Don’t use jQuery unless it’s absolutely necessary •Remember it’s sometimes more performant to use regular ol’ JavaScript •jQuery is JavaScript so there’s no harm. •How many times have you done this..

Slide 44

Slide 44 text

Eg. jQuery over-use of attr() $('a').bind(‘click’, function(){ console.log('You clicked: ' + $(this).attr('id')); }); •jQuery’s ID selector only gets to document.getElementById after parsing the selector and creating a jQuery object

Slide 45

Slide 45 text

Why not use the DOM element itself? This is faster: $('a').bind(‘click’, function(){ console.log('You clicked: ' + this.id); }); •Avoid the overhead by remembering the jQuery-way isn’t always the best way.

Slide 46

Slide 46 text

Quick note: •this.id and $(this).attr(‘id’) both return the same value but remember.. •At a lower-level, this.getAttribute(‘id’) is equivalent to $(this).attr(‘id’); •However, as the attribute stays up to date, this.id is still better to use.

Slide 47

Slide 47 text

jsPerf Performance tests •$(this).attr(‘id’) vs. this.id http://jsperf.com/ el-attr-id-vs-el-id/2 •Using the former is actually 80-95% slower than directly accessing the attribute through the DOM element.

Slide 48

Slide 48 text

5 PERFORMANCE TIP

Slide 49

Slide 49 text

CACHING IS YOUR FRIEND. •Caching just means we’re storing the result of a selection for later re-use. var parents = $(‘.parents’), //caching children = $(‘.parents’).find(‘.child’), //bad kids = parents.find(‘.child’); //good

Slide 50

Slide 50 text

So remember.. •Each $(‘.elem’) will re-run your search of the DOM and return a new collection •You can then do anything with the cached collection. •Caching will decrease repeat selections.

Slide 51

Slide 51 text

Doing just about anything with the cached collection. var foo = $(‘.item’).bind('click', function({ foo.not(this).addClass(‘bar’) .removeClass(‘foobar’) .fadeOut(500); });

Slide 52

Slide 52 text

jsPerf performance tests •Comparing the performance of cached selectors vs. repeated element selections http://jsperf.com/ns-jq-cached •Uncached selectors in these tests are anywhere up to 62% slower than their cached equivalents.

Slide 53

Slide 53 text

6 PERFORMANCE TIP

Slide 54

Slide 54 text

CHAINING •Almost all jQuery methods return a jQuery object and support chaining. •This means after executing a method on a selection, you can continue executing more. •Less code and it’s easier to write! var parents = $(‘.parents’).doSomething().doSomethingElse();

Slide 55

Slide 55 text

No-chaining vs. chaining //Without  chaining $(‘#notification’).fadeIn(‘slow’); $(‘#notification’).addClass(‘.activeNotification’); $(‘#notification’).css(‘marginLeft’,  ‘50px’); //With  chaining $(‘#notification’).fadeIn(‘slow’)                                    .addClass(‘.activeNotification’)                                          .css(‘marginLeft’,  ‘50px’);

Slide 56

Slide 56 text

jsPerf performance tests •Chained calls vs. separate calls vs. cached separate calls http://jsperf.com/jquery-chaining •Chaining is the fastest followed by cached separate calls.

Slide 57

Slide 57 text

7 PERFORMANCE TIP

Slide 58

Slide 58 text

EVENT DELEGATION •The idea that you allow events to bubble up the DOM tree to a parent element. •Important as it allows you to only bind a single event handler rather than 100s. •Works with elements in the DOM at runtime (and those injected later)

Slide 59

Slide 59 text

.bind() •Allows you to attach a handler to an event such as ‘click’, ‘mouseenter’ etc for elements •With larger sets, the browser has to keep track of all event handlers and this can take time to bind. •Doesn’t work with dynamically inserted elements.

Slide 60

Slide 60 text

.live() • Simplest form of supported event delegation • Allows you to attach a handler to an event for current and future matches of a selector • Works best for simple scenarios but has flaws (has to be at the top of the chain, fails alongside traversals) • Can’t chain to it, unlike other jQuery methods.

Slide 61

Slide 61 text

.delegate() • Allows you to specify the particular DOM element would like to bind to when attaching handlers to selections that match current/future elems. • Ensures we don’t bubble all the way up the DOM to capture an element’s target (unlike .live()) • Use when binding the same event handler to multiple elements

Slide 62

Slide 62 text

jsPerf performance tests • .live() vs .delegate() vs. delegate from body variations http://jsperf.com/jquery-delegate-vs-live-table-test/2 • .bind() vs .click() vs. live() vs. delegate() http:// jsperf.com/bind-vs-click/12 • .live() vs .live() context vs .delegate() vs. delegating to document.body http://jsperf.com/jquery-live-vs- jquery-delegate/15

Slide 63

Slide 63 text

8 PERFORMANCE TIP

Slide 64

Slide 64 text

THE DOM ISN’T A DATABASE • jQuery allows you to treat it like one but it isn’t. • Remember each DOM insertion is costly. • This means keep the use of .append (), .insertBefore(), .insertAfter() etc. to a minimum.

Slide 65

Slide 65 text

It’s also important to remember • Traversing the DOM to retrieve content or information stored in .text(), .html() etc is not the most optimal approach. • This could be in .data() instead, which allows us to attach any type of data to DOM elements safely.

Slide 66

Slide 66 text

Tip 1: Better .append() usage •Minimise use by building HTML strings in- memory and using a single .append() instead. •Multiple appends can be up to 90% slower when not appending to cached selectors and up to 20% slower with them.

Slide 67

Slide 67 text

Tip 2: Use .detach() •Works great when you’re doing heavy interaction with a node •Allows you to re-insert the node to the DOM once you’re ready •Up to 60% faster than working with undetached nodes.

Slide 68

Slide 68 text

.detach() example $(‘p’).click(function(){ $(this).toggleClass(‘off’); }); var p; $(‘button’).click(function(){ if ( p ) { /*..additional modification*/ p.appendTo(‘body’); p = null; } else { p = $(‘p’).detach(); } });

Slide 69

Slide 69 text

Tip 3: Better .data() usage •We usually attach data like this.. •But this is actually much faster.. $(‘#elem’).data(  key  ,  value  ); $.data(‘#elem’,  key  ,  value); •as there’s overhead creating a jQuery object and doing data-parsing in the first.

Slide 70

Slide 70 text

Notes •Although $.data is faster, it cannot be passed a selector, only a node. •This means $.data(elem, key, value) works where elem is already defined as an element.

Slide 71

Slide 71 text

jsPerf performance tests •.detach() vs not detaching http:// jsperf.com/to-detach-or-not-to-detach •jQuery.data vs jQuery.fn.data: http:// jsperf.com/jquery-data-vs-jqueryselection- data/11 •Multiple appends vs a single append http:// jsperf.com/string-concat-single-append-vs- multiple-append

Slide 72

Slide 72 text

9 PERFORMANCE TIP

Slide 73

Slide 73 text

UNDERSTAND LOOPS • Did you know that native for and while loops are faster than using $.each() and $.fn.each()? • jQuery makes it easy to iterate over collections, but remember it’s not always the most performant option. • Plugins like Ben Alman’s $.each2() sometimes perform better than $.fn.each

Slide 74

Slide 74 text

AVOID LOOPS IF YOU CAN. HARD, BUT NESTED DOM SELECTORS MAY PERFORM BETTER. • Unless absolutely necessary, avoid loops. They’re slow in every programming language. • If possible, use the selector engine instead to access the elements needed. • There are of course places loops cannot be substituted but try your best to optimise.

Slide 75

Slide 75 text

That said.. • Developers often need to iterate • The closure-scope provided by $.each is usually required for other reasons. • Should loops be such a pain-point you need to unroll them you’re lucky, but remember there are alternatives possible.

Slide 76

Slide 76 text

jsPerf performance tests • jQuery.each vs. for, while, reverse for, jQuery.fn.each and other loop approaches: http:// jsperf.com/jquery-each-vs-for-loop/24 • jQuery.fn.each vs Ben Alman’s .each2() http:// jsperf.com/jquery-each-vs-quickeach/3

Slide 77

Slide 77 text

10 PERFORMANCE TIP

Slide 78

Slide 78 text

Avoid constructing new jQuery objects unless necessary • Developers commonly create new jQuery objects on iterations such as the above just to access some text • Using a lower-level method like $.method() rather than $.fn.method() can help improve performance with this. $(‘a’).map(function(){ return $(this).text();}); Thanks to James Padolsey for this tip

Slide 79

Slide 79 text

$.text vs $.fn.text http://jsperf.com/jquery-text-vs-html/5

Slide 80

Slide 80 text

Notes: • Not all jQuery methods have their own single- node functions • James proposed jQuery.single() as a solution to this problem • It uses a single jQuery object for all calls to jQuery.single() and only works for single DOM elements. http://james.padolsey.com/javascript/76-bytes-for-faster-jquery/

Slide 81

Slide 81 text

Bonus Tip

Slide 82

Slide 82 text

KEEP YOUR CODE DRY • Repeating the same code increases the size of your code-base and reduces productivity • DRY (don’t repeat yourself) encourages one representation of each piece of knowledge • Keeping code minimal can also remind you that chaining, caching etc can assist with this.

Slide 83

Slide 83 text

Let’s go through a quick example.. /*Let's store some default values to be read later*/ var defaultSettings = {}; defaultSettings['carModel'] = 'Mercedes'; defaultSettings['carYear’] = 2012; defaultSettings['carMiles'] = 5000; defaultSettings['carTint'] = 'Metallic Blue';

Slide 84

Slide 84 text

Non-DRY code $('.someCheckbox').click(function(){ if ( this.checked ){ $('#input_carModel').val(defaultSettings.carModel); $('#input_carYear').val(defaultSettings.carYear); $('#input_carMiles').val(defaultSettings.carMiles); $('#input_carTint').val(defaultSettings.carTint); } else { $('#input_carModel').val(''); $('#input_carYear').val(''); $('#input_carMiles').val(''); $('#input_carTint').val(''); } });

Slide 85

Slide 85 text

DRY code var props = ['carModel', 'carYear', 'carMiles', 'carTint']; $('.someCheckbox').click(function(){ var checked = this.checked; /* What are we repeating? 1. input_ precedes each field name 2. accessing the same array for settings 3. repeating value resets What can we do? 1. programmatically generate the field names 2. access array by key 3. merge this call using terse coding (ie. if checked, set a value, otherwise don't) */ $.each(props,function(i,key){ $('#input_' + key).val(checked ? defaultSettings[key] : ''); }); });

Slide 86

Slide 86 text

THANKS. • Props to Adam Sontag, JD Dalton, Paul Irish, Timmy Willison, James Padolsey, Mathias Bynens, Matt Baker and the team @jquery • For more on me: • http://addyosmani.com • @addyosmani

Slide 87

Slide 87 text

GO BUILD AWESOME THINGS. THAT’S IT!