jQuery or Native Method? (Everyday Techniques No. 3)

Like some of you, I learned jQuery long before I really learned JavaScript. While that sounds nonsensical, consider how much new users can do with jQuery before really knowing what a DOM node is, or an anonymous function, or why "this" needs to be like "$(this)". A lot of us took the fastest path to what we needed to do (make stuff blink!) and are now filling in the gap of knowledge around it.

So to me it felt especially weird when, on a recent project, I realized I could use element.style instead of .css() to set a simple CSS property. Any JS veteran would know this, but it’s surprisingly hard knowledge to come by nowadays, when the answer to so many DOM scripting questions is simply "use jQuery/Dojo/Prototype!"

Methods like jQuery’s css() are elegant and packed with utility, but there are times you can cut around them for performance boosts. These methods have lots of overhead that includes checking for multiple DOM elements and different argument types, so if you know your element and exactly what you want to do, sometimes you might be better off trying a native method instead. I’m not saying you should always replace css() with .style — css() is readable, idiomatic to jQuery, and has clever hooks for cases like opacity — but if you’re calling it 300 times a second you might want to look at more direct alternatives.

These speed improvements won’t really make a difference if you’re calling them ten or twenty times; they only offer a real boost on really long loops, big lists, or rapidly-repeating calls.

The following are a bunch of native methods and properties (presented CAGE-MATCH STYLE) that should work cross-browser and, as some jsPerf tests indicate, can out-perform than their jQuery equivalents if used correctly.

A Challenger Appears!

Note 1: $foo in these examples represents a cached jQuery object for a single DOM node: var $foo = $('#foo');. The $foo[0] syntax is just a fast way to access the first (and only) node in the object, just like .get(0) does.

Note 2: If you don’t understand how the native alternative works, BE CAREFUL. One of the hasClass alternatives, for example, will return true if asked to check “time” and the class is “multimedia”. These aren’t all-purpose drop-ins, they’re surgical replacements for controlled situations.

.css() v .style (test)

$foo.css('left', 300); // jQuery's css() method $foo[0].style.left = '300px'; // set native property

css() is a really cool method: it takes an object literal or two arguments, it runs on multiple DOM nodes (like all major jQuery methods), and it translates sticky properties like opacity for you. But, if you don’t need any of those things, try .style: it’s nearly 2x faster in my test.

.hide()/show() v .style (test)

$foo.hide(); // surprisingly slow $foo[0].style.display = 'none'; // awwww yeah

Hide and show are a lot more complex than you’d think - they set the appropriate display (block, inline, list-item) for an element and they can even be animated. But when you just need to block/none something, .style.display might be crazy faster.

$('#') v getElementById() (test)

$('#foo'); // jQuery selector $(document.getElementById('foo')); // jQuery + DOM node

The jQuery function is brilliant - it takes a DOM node, a string of HTML, a selector, or even a null argument and parses them into hot awesomeness. But really, do you need it to break up your string and deduce that you want the #content div? The answer: no. Feeding it a DOM node is always faster.

addClass() v className (test)

// jQuery $foo.addClass('bar'); // explicitly set the class $foo[0].className = 'bar'; // add to an existing class $foo[0].className += ' bar'; 

Again, className= doesn’t do all the fancy stuff addClass does - it just sets the class attribute, without checking for existing classes or the presence of the class you’re adding. But a lot of times, that’s good enough, and it’s quite fast.

hasClass() v className (test)

// jQuery way $foo.hasClass('bar'); // matches "favoritebarometer" $foo[0].className.indexOf('bar') > -1; // only matches "bar" (' ' + $foo[0].className + ' ').indexOf(' bar ') > -1;

jQuery’s hasClass() is much more sophisticated and precise, but if you need to do a simple check really fast, checking className will do the job.

val() v value (test)

$foo.val(); // jQuery get value $foo.val('bar'); // jQuery set value $foo[0].value; // native get $foo[0].value = 'bar'; // native set

Need to get or set the value of a textarea or input? val() might be overkill, because it does a bunch of tests for other, more complex form elements. Shortcut around them with .value.

attr() v get/setAttribute() (test)

// jQuery get $foo.attr('bar'); // jQuery set $foo.attr('href', 'http://quirksmode.org'); // native get $foo[0].getAttribute('href'); // native set $foo[0].setAttribute('href', 'http://quirksmode.org'); 

In IE, watch out for the 'style' attribute, which behaves badly.

Wrap-up

JS noobs, was that helpful? JS vets, did I screw that up royally? Let me know in the comments.

Update from the comments: Eric and Anthony point out that some of these ideas work particularly well inside $.each loops and event callback, where you already have a DOM element available (this).

Doug Avery

,
Posted in Article Category: #Design & Content
on