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.
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:
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.
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");
typeof(document.getElementById("para")); // returns "object"
typeof($("p#para")); // still returns "object", not "paragraph" that we need
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