First select the element..
Before performing any DOM methods on any HTML element, it is necessary that we first select it. Selecting an element in the DOM is just like a key for a car — we can't start the car without the key!
When we select an element, we basically fetch it in the form of a JavaScript DOM object on which we can access a huge variety of properties and methods as we wish. We'll explore most of these in the next HTML DOM — HTML Elements chapter.
In this chapter, we'll introduce ourselves to an array of ways to select a single element or a bunch of elements, that are dependent on either their attribute values, tag names or parent-child relationships.
Let's begin!
Selecting by ID
Perhaps, the easiest way to select an HTML element in JavaScript is using the getElementById()
method of the document
object.
getElementById()
method, of the document
object, simply selects an element based on its HTML id
attribute.The ID of the desired element is passed to the method as a string, which then looks for it and finally returns a reference to its node, if it exists, or else the value null
.
Let's see a quick example.
Consider the following <p>
element with an id
of "p1"
:
<p id="p1">A paragraph</p>
To select this paragraph, based on its id
, we can simply write the following:
var paraElement = document.getElementById('p1');
The expression document.getElementById('p1')
here would return an HTMLParagraphElement
instance for the <p>
element shown above, which consequently gets saved in the variable paraElement
.
Let's inspect the prototype object of paraElement
:
As is evident, paraElement
is an instance of HTMLParagraphElement
.
Once we have an element node in hand, we can do a lot of different things with it.
Just to name one thing: we can retrieve the content of the element by accessing the property innerHTML
:
var paraElement = document.getElementById('p1');
// Log the element's content.
console.log(paraElement.innerHTML);
innerHTML
property, in detail, in the next HTML DOM — HTML Elements chapter.IDs in a web page are supposed to be unique and so accordingly, the method getElementById()
returns only a single element.
If we have two elements on a web page with similar id
s, getElementById()
will only return the first element.
Below shown is an example:
<p id="p1">Paragraph 1</p>
<p id="p1">Paragraph 2</p>
var paraElement = document.getElementById('p1');
console.log(paraElement.innerHTML);
As you can clearly see from the log, the selected element is the first <p>
element with the given ID.
Another important thing to note about getElementById()
, and some other methods in the following sections, is that the argument provided to it, which is the value of an HTML id
attribute, is treated case-insensitively.
That is, it doesn't matter what is the case of the actual id
attribute in the HTML source code and the value provided to getElementById()
— they both are matched ignoring the casing.
Let's take an example.
Here's the same HTML as before:
<p id="p1">A paragraph</p>
Now, let's retrieve this <p>
element by calling document.getElementById()
with the uppercase string 'P1'
, and the log its innerHTML
:
var paraElement = document.getElementById('P1');
console.log(paraElement.innerHTML);
Clearly, the element does indeed get selected, even though the ID argument provided to getElementById()
is in uppercase, whereas the corresponding id
attribute in the HTML source code is in lowercase.
Selecting by class
Apart from id
, another HTML attribute used frequently to help in selecting given elements, in CSS and JavaScript, is the class
attribute.
To fetch a bunch of elements with a given class name, we can use the getElementsByClassName()
method.
getElementsByClassName()
method, of the document
object, selects a collection of elements based on a given HTML class
attribute.Like getElementById()
, this method takes in the given class name as a string.
However, what's different is that getElementsByClassName()
returns an HTMLCollection
instance containing all the elements with the given class name; not just a single element.
getElementsByClassName
. The name implies that the method considers to select elements, not just a single element!Let's see what exactly is an HTMLCollection
.
What is an HTMLCollection
?
An HTMLCollection
instance is, more or less, like a JavaScript array.
That is, it is a sequence of elements, each of which can be accessed individually via bracket notation and where the total number of elements is denoted by the length
property.
However, note that we only say that an HTMLCollection
object is 'like' an array — it's NOT actually an array. The HTMLCollection
class doesn't inherit from Array
, and so doesn't have access to any array methods like indexOf()
, slice()
, push()
, etc.
Following the fact that getElementsByClassName()
returns back an HTMLCollection
object containing all the selected elements, we have to first individually access a particular element in this collection before we can work with it.
Let's see a quick example to help us understand everything much better.
In the HTML code shown below, we have two <p>
elements with a class of p1
:
<p class="p1">A .p1 element</p>
<p class="p1">Another .p1 element</p>
To select both these .p1
elements, we write the following statement:
var paraElements = document.getElementsByClassName('p1');
With the invocation of the method, all the elements in the DOM tree with the HTML class p1
are searched for and then placed inside an HTMLCollection
list which is returned at the end and saved in the variable paraElements
.
Notice the naming of the variable that holds the returned HTMLCollection
instance here. We call it paraElements
(i.e. it's plural) since it is a list of all paragraph elements in the document. Another good name could be paraElementList
.
Anyways, with both the .p1
elements selected, now if we want to use either of the selected elements for further processing, we have to use an index — just like we do with arrays.
For instance, if we want to select the first .p1
element, we'd write paraElements[0]
.
In the code below, we log the innerHTML
of both the .p1
elements in the HTML source code above:
var paraElements = document.getElementsByClassName('p1');
console.log(paraElements[0].innerHTML);
console.log(paraElements[1].innerHTML);
paraElements[0]
refers to the first element in the paraElements
collection; similarly, paraElements[1]
refers to the second element in the collection.
Simple.
In the example above, since there were only two <p>
elements, we managed to refer to each one of them manually. However, in general, when the elements are in large numbers, it's common, convenient and efficient to shift this logic under the hood of loops.
Working with an HTMLCollection
object in a for
loop is extremely easy — thanks to its length
property.
Below shown is the rewritten version of the code above, using a for
loop:
var paraElements = document.getElementsByClassName('p1');
for (var i = 0, len = paraElements.length; i < len; i++) {
console.log(paraElements[i].innerHTML);
}
Once again, simple. Wasn't this?
Moving on, as with getElementById()
, the argument sent to getElementsByClassName()
is also treated case-insensitively. That is, if we call getElementsByClassName('p1')
or getElementsByClassName('P1')
, they are both effectively the same.
One final thing left to be discovered in regards to getElementsByClassName()
is that the HTMLCollection
instance it returns is a live collection.
What exactly does this mean is covered in the following snippet:
What is a live HTMLCollection
?
The most interesting aspect of the HTMLCollection
class is that it is used to represent live collections of elements.
That is, if we have an HTMLCollection
instance with us at a given point of time, and then later on, modify the DOM by adding or removing any particular set of elements, the HTMLCollection
instance would automatically get updated. We won't have to again retrieve a new HTMLCollection
.
Let's illustrate this with an example.
Consider the following HTML code:
<p class="p1">A .p1 element</p>
<p class="p1">Another .p1 element</p>
And now consider the following JavaScript:
var paraElements = document.getElementsByClassName('p1');
console.log(paraElements.length);
// Add a new .p1 element to the end of <body>
var paraElement = document.createElement('p');
paraElement.className = 'p1';
document.body.appendChild(paraElement);
console.log(paraElements.length);
At the start, we call getElementsByClassName()
to select all .p1
elements in the document. This obviously returns back an HTMLCollection
instance, with the two elements shown in the HTML code above. Then we log the length of this collection, which is simply 2
.
Next up, we create another .p1
element, this time using JavaScript, in lines 6 - 8. We'll understand how to create elements using DOM methods in the next chapter.
Anyways, with the third element added to the document, by the execution of line 8, we finally log the length of the initial collection of .p1
elements again. At this point, the value logged is 3
.
We didn't make even a single change to paraElements
in the code, yet it was able to recognize the addition of a new .p1
element into the DOM tree, and consequently update itself.
This nature of HTMLCollection
means that we don't have to do create a new HTMLCollection
instance in order to get an updated version of a given collection of elements — the initial HTMLCollection
instance would automatically get updated.
For example, in the code below, the highlighted line is meaningless:
var paraElements = document.getElementsByClassName('p1');
console.log(paraElements.length);
// Add a new .p1 element to the end of <body>
var paraElement = document.createElement('p');
paraElement.className = 'p1';
document.body.appendChild(paraElement);
// No need to recompute an HTMLCollection.
paraElements = document.getElementsByClassName('p1');
console.log(paraElements.length);
Simple.
This nature of HTMLCollection
also means that we should cache the length of an HTMLCollection
instance if we want to iterate over all its elements.
For example, consider the code below:
var paraElements = document.getElementsByClassName('p1');
for (var i = 0; i < paraElements.length; i++) {
console.log(paraElements[i].innerHTML);
}
Notice the loop's condition here — it accesses the length
property of paraElements
each time it's evaluated. This, although won't cause a noticeable performance glitch, has a certain amount of overhead associated with it (which we'll cover up next in this section).
A slightly better alternative is to cache the length of the HTMLCollection
in a variable, and then use that variable in the loop's condition. In this way, each time the condition is evaluated, we would only be accessing a fixed-valued variable.
Here's a better version of the code above:
var paraElements = document.getElementsByClassName('p1');
for (var i = 0, len = paraElements.length; i < len; i++) {
console.log(paraElements[i].innerHTML);
}
So now that we know that all HTMLCollection
s are live containers for given elements, there is another similar question that comes to the mind: how is a live collection implemented by engines?
Hmm. This sure is a good question.
The thing is that every engine has its own rules for implementing live collections, but at the core there is one common and extremely simple idea used. It is discussed as follows:
How are live collections implemented?
All properties/methods of the HTMLCollection
class are internally configured to search for the underlying set of elements, each time they are accessed.
For example, whenever we access the length
property of an HTMLCollection
instance, it recomputes the set of elements using the DOM tree available at that time and then returns the length of that set.
As another example, even accessing a given element from an HTMLCollection
instance, using bracket notation (for example, paraElements[0]
) would recompute the set of elements by searching the latest DOM tree.
This approach, however, would have had a negative impact on performance in cases when the DOM tree remains the same throughout the execution of a given piece of JavaScript code working with an HTMLCollection
instance.
And that's where the idea of caching comes in.
To state is precisely, all properties/methods of the HTMLCollection
class are configured to search for the underlying set of elements each time they are accessed, given that the internal cache of the respective instance has become invalidated.
So how does an internal cache for an HTMLCollection
instance become invalidated?
Well, whenever the DOM tree is modified, the internal cache of an HTMLCollection
instance gets invalidated. Thereafter, accessing any property/method on the HTMLCollection
instance would lead to a new search over the DOM tree for the underlying set of elements.
However, if the DOM tree is not modified since the creation of an HTMLCollection
instance, its internal cache would remain valid and so likewise, accessing any property/method on it would not lead to any recomputations.
Easy, wasn't this?
Time to test your understanding of getElementsByClassName()
.
Write a program that selects all the .hidden
elements, as shown below, and then logs their innerHTML
properties one-by-one.
<section class="hidden">Some content</p>
<p>A <span class="hidden">span</span> element</p>
<div class="hidden">Some content</div>
<div>Some more content</div>
var hiddenElements = document.getElementsByClassName('hidden');
for (var i = 0, len = hiddenElements.length; i < len; i++) {
console.log(hiddenElements[i].innerHTML);
}
Selecting by tag name
Say we have no id
or class
attributes on a given set of HTML elements and still want to select them based on their tagnames.
Well, it ain't a big deal — we just ought to use the getElementsByTagName()
method.
getElementsByTagName()
method, of the document
object, selects a collection of elements based on a given HTML tag name.It operates exactly like getElementsByClassName()
, except for that it searches for elements based on given tag names (such as <p>
, <div>
, <span>
) rather than given class names.
The tag name is provided to getElementsByTagName()
, once again, as a string, which then returns back an HTMLCollection
containing all the matched elements.
For example, to select all <p>
elements, we would call document.getElementsByTagName('p')
.
As always, it's time for an example.
In the code below, we select all the <p>
elements shown, and then log each one's innerHTML
one-by-one:
<p>First paragraph</p>
<p>Second paragraph</p>
<div>A div element</div>
<p>Third paragraph</p>
var paraElements = document.getElementsByTagName('p');
for (var i = 0, len = paraElements.length; i < len; i++) {
console.log(paraElements[i].innerHTML);
}
As you might agree, the code is pretty straightforward to follow along.
getElementsByTagName()
also returns a collection, just like getElementsByClassName()
. Hence, to refer to an individual element, we have to use its index.Moving on, as with both the methods discussed above, i.e. getElementById()
and getElementsByClassName()
, the argument passed to getElementsByTagName()
is treated case-insensitively.
Hence, both getElementsByTagName('script')
and getElementsByTagName('SCRIPT')
are identical to one another.
Write a program that selects all the <section>
elements shown below, and then logs their innerHTML
property one-by-one.
<h1>Heading 1</h1>
<section>Section 1</section>
<h1>Heading 2</h1>
<section>Section 2</section>
var sectionElements = document.getElementsByTagName('section');
for (var i = 0, len = sectionElements.length; i < len; i++) {
console.log(sectionElements[i].innerHTML);
}
Mixing these together
The beauty of all the three element selection methods we saw above is that they not just operate on the document
object, but also on a given element node.
Speaking technically, all the three methods, i.e. getElementById()
, getElementsByClassName()
and getElementsByTagName()
, are defined on the Document
class and additionally on the Element
class as well.
Not surprisingly, the methods of the Document
class perform search for given elements over the entire document. In contrast, the methods of the Element
class perform search only within the given Element
instance.
In all of the examples above, we called the methods on the document
object, in which case the searching was performed over the entire HTML document.
Now in this section, we shall consider these methods on given element nodes as well, and in this way mix them together in complex ways to select nested sets of elements.
Starting with our very first example, consider the HTML below:
<div id="main">
<p class="small">A paragraph inside</p>
<p class="small">Another paragraph inside</p>
</div>
<p class="small">A paragraph outside</p>
We have a #main
container with two .small
elements inside it, in addition to a .small
element outside it.
Our job is to select the two .small
elements inside #main
.
First thing's first, we'll start by selecting the #main
element using getElementById()
on document
:
var mainElement = document.getElementById('main');
Now, since searching has to be done within this #main
element, we'll call the second selection method on it. In particular, we'll invoke getElementsByClassName()
on mainElement
in order to select both the .small
elements inside it.
This is accomplished in line 2 below:
var mainElement = document.getElementById('main');
var smallElements = mainElement.getElementsByClassName('small');
Now, let's iterate over smallElements
and log the innerHTML
of each of its items:
var mainElement = document.getElementById('main');
var smallElements = mainElement.getElementsByClassName('small');
for (var i = 0, len = smallElements.length; i < len; i++) {
console.log(smallElements[i].innerHTML);
}
As evident by the logs, smallElements
holds the two <p>
elements inside the #main
container.
Amazing!
Let's now play with another example, this time a bit more complicated then the one above.
In the snippet below, we have various .small
elements scattered over the HTML markup. The goal is to log the content of all those that are contained within .main
elements:
<div class="main">
<p class="small">Paragraph 1</p>
<p class="small">Paragraph 2</p>
</div>
<p class="small">Paragraph 3</p>
<div class="main">
<p class="small">Paragraph 4</p>
<p class="small">Paragraph 5</p>
</div>
We'll start by getting done with the first concern, i.e selecting all .main
elements:
var mainElements = document.getElementsByClassName('main');
After this, for each selected .main
element, we'll select all .small
elements within it and then log their content one-by-one:
var mainElements = document.getElementsByClassName('main');
for (var i = 0, len = mainElements.length; i < len; i++) {
// Select all .small elements inside the current .main element.
var smallElements = mainElements[i].getElementsByClassName('small');
for (var j = 0, len2 = smallElements.length; j < len2; j++) {
console.log(smallElements[j].innerHTML);
}
}
Notice the nested for
loops — the first one works on each selected .main
element, whereas the second one works on each selected .small
element. Simple.
Here's the output produced by this code:
Voila! We were successful in selecting the desired set of elements.
Now although, the code above does select the desired elements, you would surely agree that it's way too complex for even simple element selection problems.
Imagine having to select all <b>
elements inside all <div>
elements inside all .small
elements inside all .main
elements, using just the three methods discussed above. We'll need 4 for
loops iterating over the desired sets of elements, starting with .main
, and filtering out as we go deeper.
Clearly, this would be way too complex.
A better method is needed here, and indeed there is. In fact, there are two such methods — querySelector()
and querySelectorAll()
, as we shall see in the next section.
Selecting by CSS selector
As we just saw above, selecting given elements within given elements in JavaScript, solely using the methods getElementById()
, getElementsByClassName()
and getElementsByTagName()
, can quickly become a cumbersome task.
Fortunately, there's a much better way to use instead. And that is to employ the CSS selector syntax to select given elements with the help of the querySelector()
and the querySelectorAll()
methods.
querySelector()
and the querySelectorAll()
methods, of the document
object, select elements based on a given CSS selector.The CSS selector is passed in to the methods as the first argument, in the form of a string, just how we do in all of the selection methods that we've seen thus far.
The difference between these two methods is that querySelector()
selects the first matched element while querySelectorAll()
selects all the matched elements.
As with getElementsByClassName()
and getElementsByTagName()
, the method querySelectorAll()
also returns back a collection containing all the matched elements, however this collection is NOT an HTMLCollection
instance.
Rather it is a NodeList
instance.
What is a NodeList
?
A NodeList
object is very similar to an HTMLCollection
object.
It also supports the usage of bracket notation to access individual elements, and has a length
property containing the total number of elements.
However, one major difference between NodeList
and HTMLCollection
is that the former is meant to represent a collection of arbitrary nodes, not just element nodes. HTMLCollection
, as we know, is only meant to represent a collection of element nodes.
But there is a confusion here.
The querySelectorAll()
method selects only HTML elements. This means that a much more sensible class to be implemented by it would be HTMLCollection
, instead of NodeList
.
So then why does querySelectorAll()
return a NodeList
instance?
As expected, it is also indexed, has a length
property and contains elements accessible via bracket notation.
On the outskirts, it operates exactly like an HTMLCollection
object, however it has one crucial difference. This we shall explore later in the section below.
Anyways, using these two methods, we can easily and efficiently select elements with extremely complex nestings; which would otherwise take multiple lines of code if done using the methods discussed above.
Let's consider the same example above, this time to be solved in a more elegant way:
<div class="main">
<p class="small">Paragraph 1</p>
<p class="small">Paragraph 2</p>
</div>
<p class="small">Paragraph 3</p>
<div class="main">
<p class="small">Paragraph 4</p>
<p class="small">Paragraph 5</p>
</div>
We needed to select all .small
elements that were within a .main
element and log each one's innerHTML
.
Following we use the querySelectorAll()
method to solve this task:
var eleList = document.querySelectorAll(".main .small");
for (var i = 0, len = eleList.length; i < len; i++) {
console.log(eleList[i].innerHTML);
}
If we use querySelector()
rather than querySelectorAll()
here, it would return only the first matched element rather than a list.
Consider the code below:
var ele = document.querySelector(".main .small");
console.log(ele.innerHTML);
querySelector(".article:last-child")
.querySelector(selector)
is same as calling querySelectorAll(selector)[0]
.Pre-defined DOM collections
Apart from the methods we saw above to select elements, JavaScript comes equipped with a handful of properties on the document
object that have some common HTML elements pre-selected.
Below shown is the list of these properties:
document.documentElement
selects the<html>
element.document.head
selects the<head>
element.document.body
selects the<body>
element.document.scripts
selects all<script>
elements.document.links
selects all the<a>
elements.document.forms
selects all the<form>
elements.document.images
selects all the<img>
elements.
So, in order to select the <body>
element, instead of going like:
document.getElementsByTagName("body")[0];
we can simply just write:
document.body
And in this way we can select the <body>
element pretty easily.
What will the following expression evaluate to?
document.getElementsByTagName("script")[0] === document.scripts
true
false
In the coming chapters we'll be, at least, using the first three properties pretty frequently — document.documentElement
, document.head
, and document.body
. So, just make sure you have a good understanding of them.
Relationship-based fetching
In the previous HTML DOM Tree chapter we saw the DOM tree model and how various nodes are linked together via parent-child or sibling relationships.
For instance, <html>
is the parent node of <body>
, while <body>
is the child node of <html>
. Similarly, <head>
is the sibling of <body>
and so on and so forth.
Now, in this chapter, we shall use this relationship to select given elements in a document starting from another given element.
The properties we'll be exploring are parentNode
, childNodes
, nextSibling
, previousSibling
and after these children
, nextElementSibling
and previousElementSibling
.
parentNode
The parentNode
property of a given element node points to its parent.
If an element does not have a parent node, like the document
object, this property will be equal to null
.
For example, the parentNode
property on the document.body
element will point to the document.documentElement
object.
Consider the code below, where we justify this relationship:
document.body.parentNode === document.documentElement; // true
childNodes
The childNodes
property of a given element node returns a list of all its child nodes.
More specifically, childNodes
returns a NodeList
object containing all the children of a given node.
NodeList
object?Remember that the children can also be text and comment nodes, in addition to element nodes.
Consider the HTML below:
<html>
<head>
<title>HTML DOM</title>
</head>
<body>
<p>Experimenting with the DOM.</p>
</body>
</html>
The <html>
element here has five child nodes, in the following order:
- A text node
- The
<head>
element node - A text node
- The
<body>
element node - A text node
Now you may be thinking where are the three text nodes coming from.
If you notice, there are newlines and indentations after the <html>
, </head>
and </body>
tags. These are text nodes that happen to be the children of the <html>
element.
Anyways, inspecting the childNodes
property of the documentElement
object, we see that it returns exactly the result were expecting:
var children = document.documentElement.childNodes;
console.log(children.length); // 5
console.log();
for (var i = 0, len = children.length; i < len; i++) {
console.log(children[i].nodeName);
}
The nodeName
property of a text node returns the string #text
, whereas the one for an element node returns its tagname (uppercased).
As expected, we get five children — with three text and two element nodes.
If we remove all these text nodes from the markup, we would end up with only two child nodes of the <html>
element, as shown below:
<html><head><title>HTML DOM</title></head><body><p>Experimenting with the DOM.</p></body></html>
var children = document.documentElement.childNodes;
console.log(children.length); // 2
console.log();
for (var i = 0, len = children.length; i < len; i++) {
console.log(children[i].nodeName);
}
Simple as a piece of cake!
previousSibling
As we know from the previous chapter, siblings are nodes with the same parent.
Following from this, the previousSibling
property of a given node points to the sibling that comes immediately before it in the HTML markup, or else the value null
.
Consider the following code:
<html>
<head><title>HTML DOM</title></head><body>Hello.</body>
</html>
As is obvious, the previous sibling of <head>
is a text node, while the previous sibling of <body>
is the <head>
element.
Let's now see what does the previousSibling
property return for each of these elements:
var prevSib = document.head.previousSibling;
console.log(prevSib.nodeName);
prevSib = document.body.previousSibling;
console.log(prevSib.nodeName);
As expected, first we get a text node and then the <head>
element returned.
nextSibling
Now that you've understood previousSibling
, understanding the logic of nextSibling
won't be a problem.
For a given node, its nextSibling
property points to the sibling that comes immediately after it.
Let's apply this to the same code above:
<html>
<head><title>HTML DOM</title></head><body>Hello.</body>
</html>
The next sibling of <head>
is <body>
, while the next sibling of <body>
is a text node.
As before, let's also check this through code:
var nextSib = document.head.nextSibling;
console.log(nextSib.nodeName);
nextSib = document.body.nextSibling;
console.log(nextSib.nodeName);
Once again, just what we expected.
Selecting elements only
Often times, while selecting nodes via the traversal properties we saw above, one needs to make sure that the properties return elements only.
People are generally not concerned with other types of nodes. Who would want to process text or comment nodes anyway?
However, as we know, the properties childNodes
, previousSibling
and nextSibling
can all point to non-element nodes — and so to select elements only, we are just left with manual checks.
Fortunately, JavaScript comes with three special properties that operate exactly like childNodes
, previousSibling
and nextSibling
, but that take into account elements only.
They are children
, previousElementSibling
and nextElementSibling
, respectively.
As the names might suggest, these properties are meant to select only element nodes.
Let's see each one...
children
The children
property of a given element node returns an HTMLCollection
object containing all its children element nodes.
childNodes
returns a NodeList
object. This is a clear indicator of the fact that it returns a list with non-element nodes as well.Consider the code below:
<html>
<head>
<title>HTML DOM</title>
</head>
<body>
<p>Experimenting with the DOM.</p>
</body>
</html>
As we have seen above, calling childNodes
on documentElement
returns a list of five nodes. However, calling children
will return only two nodes; both elements:
var childrenElements = document.documentElement.children;
console.log(childrenElements.length); // 2
console.log();
for (var i = 0, len = childrenElements.length; i < len; i++) {
console.log(childrenElements[i].nodeName);
}
previousElementSibling
The previousElementSibling
property, of a given element node, returns the sibling element immediately coming before it.
If there isn't any such element, the property returns the value null
.
Below shown is an illustration:
<html>
<head>
<title>HTML DOM</title>
</head>
<body>
<p>Experimenting with the DOM.</p>
</body>
</html>
var prevElemSib = document.head.previousElementSibling;
console.log(prevElemSib);
prevElemSib = document.body.previousSibling;
console.log(prevElemSib.nodeName);
The previous element sibling of <head>
is null
, whereas the previous element sibling of <body>
is <head>
.
Just remember that previousElementSibling
is meant to return element nodes only; whereas previousSibling
is meant to return non-element nodes, as well.
nextElementSibling
The nextElementSibling
property, of a given element node, returns the sibling element coming immediately after it.
If there isn't any such element, it returns null
.
Below shown is an illustration:
<html>
<head>
<title>HTML DOM</title>
</head>
<body>
<p>Experimenting with the DOM.</p>
</body>
</html>
var nextElemSib = document.head.nextElementSibling;
console.log(nextElemSib);
nextElemSib = document.body.previousSibling;
console.log(nextElemSib.nodeName);
The next element sibling of <head>
is <body>
, whereas the next element sibling of <body>
is null
.
Once again remember that nextElementSibling
is meant to return element nodes only; whereas nextSibling
is meant to return non-element nodes, as well.
And this is all that we need to perfect in the HTML DOM selection world.
In conclusion
Just like we said before, selecting an element is vital and a must, before being able to play with the potential of the DOM.
Once you've perfected how to select given stuff in an HTML document and then process it one-by-one, in the case of lists, you can consider moving on to explore how to work with HTML DOM elements.
Surely, a fun avenue awaits!