Tutorial 6 - CSS: Selecting More Things
Table of contents: This tutorial
- Selecting things: A bird's eye view
- Get in the habit of using context
- Whose children are these?
- Using :nth-child selector
- A small detail about using :nth-child selector
- First and Last children
- Selecting by "Empty" element
- Hidden or Visible
- Even or Odd
Selecting things is at the very core of jQuery programming. (This will require a brief familiarity with the basics of jQuery.) And even though I already covered jQuery CSS Selectors in one of my previous tutorials, this article and the diagrams go in more detail.
Selecting things: A bird's eye view
Selecting things doesn't have to be complicated. However, there are many cases when the HTML is just way complicated. In many instances, people rush to assign many IDs or CLASSes to everything and go from there.
The ID and CLASS attributes are not the best tools in this situation. In order to swiftly navigate the DOM tree structure, jQuery presents us with very neat tools. This tutorial will outline some of the most effective selectors to use in many common situations.
At first, I started this tutorial with the focus only on input elements such as input, textarea, checkbox, radio button and select/option tags. But eventually as I continued updating it, it evolved into something much more.
I still cover input elements at the bottom of his page, but I'd like to also talk about many other ways in which you should be selecting your elements. The best idea is to go through them one by one, and follow these clear diagrams below, that I think really make a good point of what each type of a selector will do.
Once you memorize these ways, you will never want to go back to your old ID and CLASS attributes. One of the reasons for this is that each element already has a name - it's tag name.
jQuery allows us to refer to these tags by their name, and in some cases we can refer to them by not even using the name or ID of an element we want to select. How is this possible? Let's compare the most common mentality of a jQuery enthusiast to one of a more experienced programmer.
The main difference here is that when just starting out people tend to assign ID and CLASS attributes to everything. Is this the right way of doing things?
Figure 1. explains mentality of the beginner jQuery programmer because everything is assigned an ID (and in many other cases a class, though, this is not shown on the diagram).
Why is this a popular mentality of someone who just started out using jQuery? Well, it's probably because they only learned how to select elements by ID or a Class name and refused to learn other types of selections one can make using advanced selectors and DOM traversal techniques that jQuery has in its arsenal.
What's up with the "blank" mentality in Figure 2? A more experienced jQuery developer will look at the DOM as a clean slate. There is no need to assign an ID to every single element just to refer to it. In fact, this is possible because you can select any element without assigning a single ID or CLASS attribute.
But at the same time, we know that referring to an element by ID is the fastest way to select an element. We have already discussed how you can use context, the second parameter of the jQuery ($) function to select a proper context and speed up your jQuery code. So, IDs should not be completely avoided. But they should not always be considered as the only method of element identification either.
Because of this...it should be understood that a versatile jQuery programmer must create a balance between the two mentalities, not to ignore one of them completely. However, he or she must first consider Figure 2 and ask the question: Does assigning an ID to an element in your DOM really improve your DOM? Or does it complicate it? Are there performance benefits of using ID attributes? Or it doesn't matter because you are not planning on selecting many elements in the first place?
Can you avoid using IDs or classes? In many, if not most, cases it's very likely that you can! The rest of this tutorial will use this knowledge to show you how to effectively use these methods to select pretty much anything you want without creating an ID or CLASS infestation in your HTML code for every single element. But first...
Get in the habit of using context
Thinking about context, or the confinement area of your selection is not always about speed, but rather, it's about organizing your code and the way you think about CSS selectors. Let's take a look at the following diagram, in which two similar DIV elements serve as the context of a selection.
Notice that the contents of each of the two DIV blocks defined as possible contexts are arbitrary elements of any type. This is the "clean slate" mentality of an experienced jQuery developer. We want to avoid assigning IDs to most of them, unless you really are certain that it will make your elements easier to select.
When it comes to choosing a selection within a context element, we need to think of assigning an ID to the context element. The diagram above is just a simple example. However, you will need to use the code from your real-life HTML page in order to determine what that context should be in the first place... that code may not look so simple at all. Especially this is true when you are working with someone else's code and seeing it for the first time.
You may think, well, what is the benefit of not using IDs everywhere? The benefit is mostly in the alternative way of thinking about your code. It has no other advantages whatsoever. But this alternative, "careful" way of thinking will determine your efficiency as a jQuery developer in the long run.
Don't believe me? Let's take a look at all of the ways in which this new mentality can be helpful.
Whose children are these?
If we are to avoid using IDs completely, except when we need to assign them to a context, how do we navigate the DOM?
We do so by using parents and children. But which elements are children of which parent elements? How does the DOM determine that? Well, that's
. Just like in the real world, children belong to parents. But those parents can also be children of their parents, and so on. It's just a matter of perspective. Let's take a look at this diagram that explains relationships between parents and children:
If each box on this diagram represents an HTML element, then the first child of the body tag is also the parent of all green children.
The second child of the body tag is also the parent of all yellow children.
Each individual yellow "child" is also an HTML element. Then black children 1, 2 and 3 are the children of one of the yellow child whose parent is the second child of body tag. Trivial stuff when you have a visual guide.
Using :nth-child selector
Instead of using IDs for everything we will use the :nth-child selector that works on tag names.
Let's assume that we have a DIV and that we have a few other elements stored inside it; in particular a DIV element, a SPAN element followed by 3 more DIV elements:
The :nth-child(2) will select the span element. Index 2, in this case is the literal 2nd child. Indexing, in this case, does not begin with 0.
A small detail about using :nth-child selector
The jQuery's implementation of nth-child selector is somewhat peculiar when dealing with children of different types in the same parent. If we take the diagram from above as an example and create a selector such as this one:
$("body div div:nth-child(2)")
It will not select anything at all. The second DIV child (whatever that may mean in this case?) simply doesn't exist. Because the second child is an element of type SPAN, not DIV. Logically, you would think this selector would choose the DIV after the SPAN element. But that would be wrong, because that DIV element can be rightfully selected as div:nth-child(3).
Had all of the elements been of type DIV, then this selector would actualy work!
You can see now that this selector prioritizes the notion of child, not what type that child is... but that it is a child. It's just how it works when different types of elements happen to be children of the parent element. This is not a bug.
Next, let's explore the possibility of making a selection using the first or last child keywords.
First and Last children
There are three different ways in which jQuery allows selecting HTML elements by first or last element in a container. Consider the following diagram:
It doesn't matter which way you choose. However, the colon character allows us to "glue" the selector to a particular element kind. The example above does not explicitly state what type of an element we are selecting. In order to do that, you would write something like div:last.
If you are going to explicitly specify the type of an element you want to choose, such as DIV (not shown in the diagram) notice that there would be no space between div and the colon character.
If there was a space between div and colon, however, it would mean a completely different thing. Remember that the space is a selector in its own right, it means "go inside the element", so the selector such as div :last would seek the last child of an element located inside an arbitrary div block anywhere on the page - it would even select all children of any DIV element on the page.
Selecting by "Empty" element
We can also select elements that are empty. An empty element is an element that doesn't contain any text or other elements.
Note that even though there is clearly a value assigned to an input field, it will still be selected. Why? I don't know, that's just the way it works. Did the developers of jQuery miss something here? It seems that being able to select an empty input element would be more valuable than selecting an empty DIV element. This is true of February 10th, 2012. Will they fix this in later builds?
Hidden or Visible
This is self-explanatory. You can select an element that is hidden (has visibility:hidden) or an element that is visible by using the following selectors:
$(':hidden') // select all hidden elements
$(':visible') // select all visible elements
Even or Odd
One of the unique types of selectors jQuery offers are the :even and :odd selectors. Here is how they work:
You can use :even or :nth-child(even) interchangeably, they select absolutely the same things.
I am planning on adding descriptions of the following subjects to this article at a later time.