Query Selectors
The three methods we had used so far to find elements in the DOM getElementsByTagName
, getElementsByClassName
and getElementById
can be quite limiting when it comes to selecting elements that don't necessarily have id, or class but is contained in a particular nesting structure. The selector syntax of CSS rules are very good for selecting particular elements that don't have id or class. That's why the DOM api provides a method on the document and nodes called querySelectorAll
that will let you select elements based on the CSS selector syntax. Suppose we have the following HTML file.
<!doctype html>
<html>
<head>
<title>My title</title>
</head>
<body>
<p>Paragraph 1</p>
<div>
<p>Paragraph 2</p>
<p>Paragraph 3</p>
<div>
<p>Paragraph 4</p>
</div>
</div>
</body>
</html>
If we want to get all the paragraph elements. We could use getElementsByTagName('p')
, it does the job very well. But if we want to only select paragraph 2 and 3. Using the the get elements methods will require rather troublesome code.
var paragraph2and3 = [].slice.call(document.getElementsByTagName('div')[0].children).filter(function(el) {
return el.tagName === 'P';
});
console.log(paragraph2and3);
// -> [ HTMLParagraphElement {}, HTMLParagraphElement {} ]
Or we can use querySelectorAll
.
var paragraph2and3ViaQuery = document.querySelectorAll('body > div > p');
console.log(paragraph2and3ViaQuery);
// -> NodeList { 0: HTMLParagraphElement {}, 1: HTMLParagraphElement {} }
Notice that the NodeList
object returned by querySelectorAll
method is not live. Which means as the DOM changes, the content of the object does not update automatically. Therefore, after you manipulate the DOM, you need to make a new request to get the most up to date elements.
The sister method querySelector
works similarly, except it will only return the first matching element or null.
var paragraph4 = document.querySelector('div > div > p');
console.log(paragraph4);
// -> HTMLParagraphElement {}