jquery performance tips

87
  jQuer y Prov en TIPS & TRICKS  WITH ADD Y OSMANI IMAGES COPYRIGHT HASBRO AND TONKA, 1935-2011. PERFORMANC  E and Mr. Monopoly 

Upload: kanjeng-demank

Post on 03-Nov-2015

17 views

Category:

Documents


0 download

DESCRIPTION

Boosting jQuery for faster performance

TRANSCRIPT

  • jQuery Proven

    TIPS & TRICKS WITH ADDY OSMANI

    IMAGES COPYRIGHT HASBRO AND TONKA, 1935-2011.

    PERFORMANCE

    and Mr. Monopoly

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

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

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

  • Enough of that..

    LETS START!

  • WHY DOES PERFORMANCE MATTER?

    Apps should be snappy, not sloppy.Best practices offer optimal approaches

    to solving problems.

    If we dont follow them, browsers can end up having to do more work.

  • MORE WORK = MORE MEMORY USE =SLOWER APPS.

  • TODAY, ALL OF THESESLIDES COME WITH PERFORMANCE TESTS.Not just saying X is faster...were proving it too.

  • 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!

  • Example of test output

    http://jsperf.com/jquery-tree-traversing

    Anyone can tell what the fastest and slowest snippets are.

  • 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.

  • 1PERFORMANCE TIP

  • 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.

  • MOST POPULAR SITES USING JQUERY ON THE GOOGLE CDN

    Stats from Scott Mitchell

    Old

  • 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!

  • WHY?

    Older versions wont 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.

  • 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

  • 1.6.x improvements

    .attr() performance improvedhttp://jsperf.com/attr-vs-attrhooks http://jsperf.com/valhooks-vs-val/2

    .val() faster in 1.6.x

  • 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 youre using and youll be fine

  • 2PERFORMANCE TIP

  • KNOW YOUR SELECTORS

    All selectors are not created equally Just because a selection can be made in

    many ways, doesnt mean each selector is just as performant

    Do you know what the fastest to slowest selectors are?

  • Fast: ID & Element Selectors

    ID and element selectors are the fastestThis is because theyre backed by native

    DOM operations (eg. getElementById()).

    $(#Element, form, input)

  • 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

  • Slowest: Pseudo & AttributeSelectors

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

  • 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 dont such as :first, :last, :eq etc.

    Valid selectors have a better chance of using it.

  • 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

  • Pseudo-selectors are powerful..but slow, so be carefulwhen using them.

    BUT IM TOO PRETTY TO GOTO JAIL!

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

  • 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!.

  • 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

  • 3PERFORMANCE TIP

  • UNDERSTAND PARENTS AND CHILDREN1) $(.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

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

  • .find()

    This is the fastest of the entire set. Ill explain why shortly.

    2) $parent.find(.child).show();

  • Immediate children

    Internally uses $.sibling and JavaScripts nextSibling() to find nodes following other nodes in the same tree.

    ~50% slower than the fastest option

    3) $parent.children(.child).show();

  • CSS child combinator selector

    Uses a child combinator selector, however Sizzle works from right to left.

    Bad as it will match .child before checking its a direct child of the parent.

    ~70% slower than the fastest option

    4) $(#parent > .child).show();

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

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

  • The fastest option is..

    The parent selector is already cached here, so it doesnt 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();

  • Its 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 youre trying to achieve.

  • jsPerf performance tests

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

  • 4PERFORMANCE TIP

  • Dont use jQuery unless its absolutely necessary

    Remember its sometimes more performant to use regular ol JavaScript

    jQuery is JavaScript so theres no harm.How many times have you done this..

  • Eg. jQuery over-use of attr()

    $('a').bind(click, function(){console.log('You clicked: ' + $(this).attr('id'));});

    jQuerys ID selector only gets to document.getElementById after parsing the selector and creating a jQuery object

  • 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 isnt always the best way.

  • 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.

  • 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.

  • 5PERFORMANCE TIP

  • CACHING IS YOUR FRIEND.

    Caching just means were storing the result of a selection for later re-use.

    var parents =$(.parents), //caching

    children = $(.parents).find(.child), //bad

    kids = parents.find(.child); //good

  • 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.

  • Doing just about anything with the cached collection.

    var foo = $(.item).bind('click', function({ foo.not(this).addClass(bar) .removeClass(foobar) .fadeOut(500);});

  • 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.

  • 6PERFORMANCE TIP

  • 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 its easier to write!

    var parents =$(.parents).doSomething().doSomethingElse();

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

  • 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.

  • 7PERFORMANCE TIP

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

  • .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.

    Doesnt work with dynamically inserted elements.

  • .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)

    Cant chain to it, unlike other jQuery methods.

  • .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 dont bubble all the way up the DOM to capture an elements target (unlike .live())

    Use when binding the same event handler to multiple elements

  • 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

  • 8PERFORMANCE TIP

  • THE DOM ISNT A DATABASE

    jQuery allows you to treat it like one but it isnt. Remember each DOM insertion is costly. This means keep the use of .append

    (), .insertBefore(), .insertAfter() etc. to a minimum.

  • Its 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.

  • 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.

  • Tip 2: Use .detach()

    Works great when youre doing heavy interaction with a node

    Allows you to re-insert the node to the DOM once youre ready

    Up to 60% faster than working with undetached nodes.

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

  • 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 theres overhead creating a jQuery object and doing data-parsing in the first.

  • 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.

  • 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

  • 9PERFORMANCE TIP

  • 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 its not always the most performant option.

    Plugins like Ben Almans $.each2() sometimes perform better than $.fn.each

  • AVOID LOOPS IF YOU CAN. HARD, BUT NESTED DOM SELECTORS MAY PERFORM BETTER.

    Unless absolutely necessary, avoid loops. Theyre 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.

  • 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 youre lucky, but remember there are alternatives possible.

  • 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 Almans .each2() http://jsperf.com/jquery-each-vs-quickeach/3

  • 10PERFORMANCE TIP

  • 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

  • $.text vs $.fn.text

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

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

  • Bonus Tip

  • KEEP YOUR CODE DRY

    Repeating the same code increases the size of your code-base and reduces productivity

    DRY (dont 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.

  • Lets 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';

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

  • DRY codevar 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] : ''); });});

  • 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

  • GO BUILD AWESOME THINGS.THATS IT!