Helpful jQuery Tricks, Notes, and Best Practices Part II
01.20.2012   |   0comment

jQuery LogoContinuing the jQuery pain fun of some tips I’ve learned over the years… Read Part I Here

Don’t Abuse $(this)
Without knowing about the various DOM properties and functions, it can be easy to abuse the jQuery object needlessly. For instance:

$('#someAnchor').click(function() {
    alert( $(this).attr('id') );
});

If our only need of the jQuery object is to access the anchor tag’s id attribute, this is wasteful. Better to stick with “raw” JavaScript.

$('#someAnchor').click(function() {
    alert( this.id );
});

Please note that there are three attributes that should always be accessed, via jQuery: “src,” “href,” and “style.” These attributes require the use of getAttribute in older versions of IE.

// jQuery Source
var rspecialurl = /href|src|style/;
// ...
var special = rspecialurl.test( name );
// ...
var attr = !jQuery.support.hrefNormalized && notxml && special ?
    // Some attributes require a special call on IE
    elem.getAttribute( name, 2 ) :
    elem.getAttribute( name );

Multiple jQuery Objects
Even worse is the process of repeatedly querying the DOM and creating multiple jQuery objects.

$('#elem').hide();
$('#elem').html('bla');
$('#elem').otherStuff();

Hopefully, you’re already aware of how inefficient this code is. If not, that’s okay; we’re all learning. The answer is to either implement chaining, or to “cache” the location of #elem.

// This works better
$('#elem')
  .hide()
  .html('bla')
  .otherStuff();  

// Or this, if you prefer for some reason.
var elem = $('#elem');
elem.hide();
elem.html('bla');
elem.otherStuff();

jQuery’s Shorthand Ready Method
Listening for when the document is ready to be manipulated is laughably simple with jQuery.

$(document).ready(function() {
    // let's get up in heeya
});

Though, it’s very possible that you might have come across a different, more confusing wrapping function.

$(function() {
    // let's get up in heeya
});

Though the latter is somewhat less readable, the two snippets above are identical. Don’t believe me? Just check the jQuery source.

// HANDLE: $(function)
// Shortcut for document ready
if ( jQuery.isFunction( selector ) ) {
    return rootjQuery.ready( selector );
}

rootjQuery is simply a reference to the root jQuery(document). When you pass a selector to the jQuery function, it’ll determine what type of selector you passed: string, tag, id, function, etc. If a function was passed, jQuery will then call its ready() method, and pass your anonymous function as the selector.

Keep your Code Safe
If developing code for distribution, it’s always important to compensate for any possible name clashing. What would happen if some script, imported after yours, also had a $ function? Bad juju!

The answer is to either call jQuery’s noConflict(), or to store your code within a self-invoking anonymous function, and then pass jQuery to it.

Method 1: NoConflict

var j = jQuery.noConflict();
// Now, instead of $, we use j.
j('#someDiv').hide();  

// The line below will reference some other library's $ function.
$('someDiv').style.display = 'none';

Be careful with this method, and try not to use it when distributing your code. It would really confuse the user of your script! :)

Method 2: Passing jQuery

(function($) {
    // Within this function, $ will always refer to jQuery
})(jQuery);

The final parens at the bottom call the function automatically — function(){}(). However, when we call the function, we also pass jQuery, which is then represented by $.

Method 3: Passing $ via the Ready Method

jQuery(document).ready(function($) {
 // $ refers to jQuery
});  

// $ is either undefined, or refers to some other library's function.

Be Smart

Remember — jQuery is just JavaScript. Don’t assume that it has the capacity to compensate for your bad coding. :)

This means that, just as we must optimize things such as JavaScript for statements, the same is true for jQuery’s each() method. And why wouldn’t we? It’s just a helper method, which then creates a for statement behind the scenes.

// jQuery's each method source
    each: function( object, callback, args ) {
        var name, i = 0,
            length = object.length,
            isObj = length === undefined || jQuery.isFunction(object);  

        if ( args ) {
            if ( isObj ) {
                for ( name in object ) {
                    if ( callback.apply( object[ name ], args ) === false ) {
                        break;
                    }
                }
            } else {
                for ( ; i < length; ) {
                    if ( callback.apply( object[ i++ ], args ) === false ) {
                        break;
                    }
                }
            }  

        // A special, fast, case for the most common use of each
        } else {
            if ( isObj ) {
                for ( name in object ) {
                    if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
                        break;
                    }
                }
            } else {
                for ( var value = object[0];
                    i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
            }
        }  

        return object;
    }

Awful

someDivs.each(function() {
    $('#anotherDiv')[0].innerHTML += $(this).text();
});

The above:

  1. Searches for anotherDiv for each iteration
  2. Grabs the innerHTML property twice
  3. Creates a new jQuery object, all to access the text of the element.

Better

var someDivs = $('#container').find('.someDivs'),
      contents = [];  

someDivs.each(function() {
    contents.push( this.innerHTML );
});
$('#anotherDiv').html( contents.join('') );

This way, within the each (for) method, the only task we’re performing is adding a new key to an array…as opposed to querying the DOM, grabbing the innerHTML property of the element twice, etc.

This next tip is more JavaScript-based in general, rather than jQuery specific… The point is to remember that jQuery doesn’t compensate for poor coding.

Document Fragments

While we’re at it, another option for these sorts of situations is to use document fragments.

var someUls = $('#container').find('.someUls'),
    frag = document.createDocumentFragment(),
    li;  

someUls.each(function() {
    li = document.createElement('li');
    li.appendChild( document.createTextNode(this.innerHTML) );
    frag.appendChild(li);
});  

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

The key here is that there are multiple ways to accomplish simple tasks like this, and each have their own performance benefits from browser to browser. The more you stick with jQuery and learn JavaScript, you also might find that you refer to JavaScript’s native properties and methods more often. And, if so, that’s fantastic!

jQuery provides an amazing level of abstraction that you should take advantage of, but this doesn’t mean that you’re forced into using its methods. For example, in the fragment example above, we use jQuery’s each method. If you prefer to use a for or while statement instead, that’s okay too!

With all that said, keep in mind that the jQuery team have heavily optimized this library. The debates about jQuery’s each() vs. the native for statement are silly and trivial. If you are using jQuery in your project, save time and use their helper methods. That’s what they’re there for! :)

Tags: ,

Leave a Comment