5 Little Known Details About jQuery Methods

Aurelio De Rosa
Share

jQuery is the most used JavaScript library in the world, but all of us already knew that. Despite the fact that in recent months a lot of criticisms have been raised, it still attracts a lot of attention among developers. Whether you’re a jQuery beginner or a JavaScript expert (Dave Methvin and other team members are excluded), you might not know some peculiarities of jQuery. In this article we’ll discuss five of them.

Returning false in Event Binders

As we all know, jQuery’s first aim is to standardize the way different browsers behave using a unified set of methods. Where possible, jQuery enhances the features of a browser, integrating those that aren’t supported natively. Think of the selectors that you can use thanks to jQuery that aren’t natively supported by Internet Explorer 6 and 7 (the attribute selectors, :not, :last-child, and so on).

Sometimes, although these are rare exceptions, jQuery slightly diverges from standard behavior. An example is that returning false in a jQuery event handler (for example one attached to on()) is the same as calling:

event.stopPropagation();
event.preventDefault();

On the contrary, returning false in native event handlers like addEventListener() is equivalent to writing:

event.preventDefault();

This behavior can be proven by taking a look at the relevant part of jQuery’s source:

if ( ret !== undefined ) {
   if ( (event.result = ret) === false ) {
      event.preventDefault();
      event.stopPropagation();
   }
}

Pseudo-selectors Do More Than You Think

In the jQuery documentation of many pseudo-selectors you can find the following note (in this case in relation to the :checkbox pseudo-selector):

$( “:checkbox” ) is equivalent to $( “[type=checkbox]” ). As with other pseudo-class selectors (those that begin with a “:”) it is recommended to precede it with a tag name or some other selectors; otherwise, the universal selector (“*”) is implied. In other words, the bare $(‘:checkbox’) is equivalent to $( “*:checkbox” ), so $( “input:checkbox” ) should be used instead.

Now, let’s take a look at the actual code in jQuery:

function createInputPseudo( type ) {
	return function( elem ) {
		var name = elem.nodeName.toLowerCase();
		return name === "input" && elem.type === type;
	};
}

As you can see from the code, the documentation is slightly incorrect. $(':checkbox') is actually equivalent to $('input[type="checkbox"]') with regards to what it searches (note the name === "input"), but tests all the elements of the page as if you called it specifying the universal selector or nothing at all.

With this in mind, you may be tempted to not prepend the element selector anymore when using filters like this and write:

var $checkboxes = $(':checkbox');

However, for performance reasons, it’s still worth it to use it so that jQuery won’t scan every element of the page. Thus, you should still write a statement like:

var $checkboxes = $('input:checkbox');

jQuery.type()

The title of this section may already be something new to you. Did you know that jQuery has a method to determine the internal JavaScript [[Class]] of an object?

Even if you already knew this method, what you may ignore is that it’s really different from the native JavaScript typeof operator. In fact, jQuery.type() returns a more precise indication of the argument passed. Let’s see some examples:

// prints object
console.log(typeof null);

// prints object
console.log(typeof [1, 2, 3]);

// prints object
console.log(typeof new Number(3));

Using jQuery.type() to test the same arguments, we have:

// prints null
console.log($.type(null));

// prints array
console.log($.type([1, 2, 3]));

// prints number
console.log($.type(new Number(3)));

So, if you’re developing a jQuery plugin you may want to employ jQuery.type() to have a more precise idea of the type of parameters you’re dealing with.

attr() can removeAttr()

I know the title of this section may sound bizarre at least, but it asserts nothing but the truth. For those of you who don’t know the jQuery’s attr() method, it retrieves the value of an attribute for the first element in the set of matched elements, or sets one or more attributes for every matched element.

Although you might ignore this behavior, in addition to a Number or a String, the attr() method can also accept null as its second argument. When this happens, it acts as its counterpart method: removeAttr(). The latter, as the name suggests, removes an attribute from each element in the set of matched elements.

Don’t believe me? Let’s take a look at the relative part of the source code:

attr: function( elem, name, value ) {
    ...
    if ( value !== undefined ) {

        if ( value === null ) {
		    jQuery.removeAttr( elem, name );
    ...
}

As you can see, the method tests if the given value is defined (if ( value !== undefined )) and then explicitly check if it’s null, in which case calls the removeAttr() method.

This can be convenient when you have to set or remove an attribute based on a condition and you don’t want to branch your code. So, for example you can write:

$(selector).attr(anAttr, condition ? value : null);

instead of

condition ? $(selector).attr(anAttr, value) : $(selector).removeAttr(anAttr);

Should you really use this trick in your code or not? The decision it’s up to you but if I were you, I would not use it for clarity of the code. Currently this behavior is not documented and there is a discussion about it, in case you’re interested.

Turning Array-like Objects Into Arrays

As you may know JavaScript has types, like nodeList or the arguments variable within a function, that are similar to arrays, but not arrays. This means that we can access their elements using an array-like notations (for example arguments[0]) but we can’t use array methods such as forEach() and join().

Let’s say we have a nodeList of DOM elements retrieved as follows:

var list = document.getElementsByClassName('book');

We want to iterate over this array-like object using the forEach() method. If we call forEach() directly on the variable (list.forEach(...)) we obtain the error: “Uncaught TypeError: undefined is not a function”. To avoid this issue, one of the most used techniques is to use the prototype property and the call() method as shown below:

Array.prototype.forEach.call(list, function() {...});

Alternatively, you can write:

[].forEach.call(list, function() {...});

Whichever solution you choose, it’s not very elegant to read or write. Fortunately for us, jQuery comes to our rescue. Thanks to the jQuery.makeArray() method we can simply write:

$.makeArray(list).forEach(function() {...});

Much better, isn’t it?

Conclusions

What you can learn from this article, apart from these five topics, is that even an amazing and solid project like jQuery isn’t perfect. It has bugs and documentation issues, and the only source of trust to know what a method does is its source. Well, actually even the code may lie by differing from the developer’s intentions, but this is another story.

Another lesson is that you should be curious about the frameworks and the libraries you adopt, read the source from time to time, and try to learn new useful tricks and techniques as much as you can.

As a final suggestion, if you love jQuery, as I do, contribute to the project. Even the report of a bug or fixing a small documentation issue can make a huge difference for millions of developers.

Oh, and in case you wonder how I know these subtle details, the reason is because I’ve written a couple of books about jQuery, and because I follow the jQuery issue trackers. :)