Building the New Next and Prev jQuery Methods

A jQuery CSS selection hierarchy is separated by the space character. The following code doesn’t select just any paragraph. But the paragraph within a table, within a tr, within a td tag. These “within’s” are indicated by the often overlooked space character: which in its own right is an instruction to the internal CSS selector processor.

$("table tr td p");

We know that when we select an HTML element using a CSS selector, there is a distinction between the hierarchy (elements that lead to the target element) and the target element itself, which is the last selector in the hierarchy:

$("table tr td");    // Select all td's in all tables (table and tr will not be selected)
$("table tr td:last input");    // Select all input elements within the last td in a table

That sounds simple. But there is one more detail that is often overlooked.

Selecting next and previous children

Being mindful of the type of the target element is not necessary here. All children, regardless of type count. This may sound like a small detail but it can cause confusion if not fully understood.

After I learned the basics of jQuery methods next and prev, and conceptually understood them, I still sometimes find myself running into some inconsistencies when using them. Below I break down the problem into basic blocks anyone can easily understand.

The documentation says: Just use next and prev to iterate to the next or previous element counting from the currently selected target element.

But here is the key. The previous element does not have to share the type of the selected element.

In both cases the jQuery selector ignores the type of the target element. The type of the element becomes irrelevant. Let’s take a look.

Considering we have this HTML:

<table>
<tbody>
<tr>
<td>table 1</td>
</tr>
</tbody>
</table>

<table>
<tbody>
<tr>
<td>table 2</td>
</tr>
</tbody>
</table>

$("table").prev(); // Select the table, then select the previous table.
$("table").prev().css("background", "red"); // Select the table, then select the previous table and color its background red. Note, the first table will be affected, but not the second one. If the page contains only one table, the red background will not be applied to it at all. If the page contains two tables, only the first one will be affected (because it is the previous table counting back in hierarchy from the second table.) The first table is selected too, but because there is no previous table before it, there is nothing to apply the CSS code to -- and method prev() would not return any elements in that case at all.
$("table tr td").prev(); // Select each td in any and all tables, and select only the previous td counting from it. In a way, the target element is now indicated by the prev method. Not the last element (td) in selector's hierarchy as it was in previous examples.

Note in the code above, the target element is no longer td. So this time, the prev method selects the previous td (not table, like in the example we’ve just seen above it.) The target element that is selected becomes the previous td itself.

But here is the catch. The method prev would select the previous element within the table. Even if it is not of type td! For example, it would select th too. As long as it is the previous child.

What happens here? The type of the target element is not preserved. Similarly, if you used the next() method, the next element of any type in the hierarchy would be selected (counting from the currently selected one, which, only in this case any and all td elements in any and all tables on the page.) Whatever is the last element, that is the element we will be iterating forward and backward with these methods.

The circumstances:

In normal circumstances, next and prev are used to iterate through the next and previous elements of the same type. Or at least it may only seem so. What happens if the previous element is not of the type td? Will it not be selected? Consider this HTML:

<span>A text span</span>

Paragraph1

What will happen if we select a paragraph () and then use prev to go a step back? Will it still select the previous element even though it is of type span? Let’s do an experiment:

$("p").prev().css("background", "red"); // What will happen?

What will happen is that span turns red. This proves that the type of the element prev selects is completely irrelevant. What I mean is, that if you have a hierarchy of the same elements (let’s say a number of

‘s or a series of LI tags in a row — that happens often!) then you most definitely should use next and prev. And the outcome will always be predictable. But if the elements are of not the same type, then the results are not predictable. Remember we do live in a dynamic DOM world (who knows? Sometimes DOM is modified on the fly, and the HTML structure changes, so your next/prev methods will no longer select quite the same element before and after the DOM change!)

So why am I breaking this down in such detail? That’s because it is important. We are not dealing just with next and prev methods. But with the structure of HTML itself, which can have any form, either determined by us or not. That’s the key. It can be dynamic. And in this one case I outline below, it can definitely be confusing. Let’s see.

<span>a</span>
b
<span>c</span>

$("span").next().css("background", "red");

In this case only the p tag is colored red. Why can this be confusing? Because the CSS selector $(“span”) does not even talk about paragraphs. And the next element can be anything.

Most people don’t pay any attention to this and try to “calibrate” their selectors on the fly. Something doesn’t work? Add or remove a space, try another method, swap the methods around, etc. But knowing that the selector does not retain the type of the target element when it comes to next and prev methods is great knowledge in our hands.

I see a situation in which it would be useful to be able to select the previous or next element of the same type. However, jQuery itself does not offer such a solution to us. We could use a filter like this:

$("p").prev("p").css("background", "red");

But that will select a previous

element only if it is indeed a

element. If the previous element is not a p element, then none will be selected even if there are paragraph elements prior to it (past a few non-p elements). And it will not “skip over” any number of non-p elements to find one of type

.

What can we do? Let’s create our own plugin that does that. Now that would be interesting. Let’s do it! We will use the $.extend function to create our plugin. It will select all next elements within the hierarchy, skipping over however many non-p elements. In other words, it will find that next element no matter what, if it exists within the same child hierarchy.

Preparation for our custom plugin: The type-aware “next” method.

In this section you will learn about a few JavaScript tricks by creating a simple plugin. What goes into the creation of the plugin is relevant to script programming in general.

We want to create an alternative to the existing jQuery methods next and prev. But with a custom condition. The next element must be of the same type as the one specified with the selector itself. I called the new methods next_type and prev_type.

// Select the next p tag after the one being selected
$("p").next_type().css("background", "red");

First, we need to write a custom helper function. The way it works is quite simple. We are given an array of HTML element objects. We have a seed, which is an element of a certain type such as paragraph tag, span tag, etc. In JavaScript we have a function typeof that determines the type of an element:

    typeof(document.getElementById("para"));  // returns "object"

…but there is a small problem. Using typeof on a JavaScript object… or even on a jQuery object returned from the $ function will not identify the element’s type with enough precision that we need because what is returned is a custom JavaScript object (aka the jQuery object.):

typeof($("p#para"));   // still returns "object", not "paragraph" that we need

We can solve this case by turning to little known JavaScript property called tagName:

document.getElementById("para").tagName;    // Now it returns P... just what we need

jQuery simplified getting tagName by offering its own way of getting the value. In order to do that we will use the method $.fn.prop(‘tagName’); But first let’s do a brief experiment:

    
hello
    <b>again</b>
    <span>a span</span>
    
second
    
third

Keeping this HTML in mind, let’s do the following to select all elements using the star (*) selector:

    var all = $("*");    // Select absolutely all elements

We have 5 HTML elements, so the length of the returned object should say 5, right?

    alert(all.length);    // 14!

Wrong! That’s because the star (*) selector selects everything, including the tag, the

jQuery Tutorials