searching elements: make more modern
This commit is contained in:
parent
b8eb04dfb6
commit
092f739c1c
1 changed files with 121 additions and 128 deletions
|
@ -1,13 +1,14 @@
|
||||||
# Searching: getElement* and querySelector*
|
# Searching: getElement*, querySelector*
|
||||||
|
|
||||||
DOM navigation properties are great when elements are close to each other. What if they are not? How to get an arbitrary element of the page?
|
DOM navigation properties are great when elements are close to each other. What if they are not? How to get an arbitrary element of the page?
|
||||||
|
|
||||||
There are additional searching methods for that.
|
There are additional searching methods for that.
|
||||||
|
|
||||||
## document.getElementById or just id
|
## document.getElementById or just id
|
||||||
|
|
||||||
If an element has the `id` attribute, then there's a global variable by the name from that `id`.
|
If an element has the `id` attribute, then there's a global variable by the name from that `id`.
|
||||||
|
|
||||||
We can use it to access the element, like this:
|
We can use it to immediately access the element no matter where it is:
|
||||||
|
|
||||||
```html run
|
```html run
|
||||||
<div id="*!*elem*/!*">
|
<div id="*!*elem*/!*">
|
||||||
|
@ -24,7 +25,9 @@ We can use it to access the element, like this:
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
That's unless we declare the same-named variable by our own:
|
The behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), but it is supported mainly for compatibility. The browser tries to help us by mixing namespaces of JS and DOM. Good for very simple scripts, but there may be name conflicts. Also, when we look in JS and don't have HTML in view, it's not obvious where the variable comes from.
|
||||||
|
|
||||||
|
If we declare a variable with the same name, it takes precedence:
|
||||||
|
|
||||||
```html run untrusted height=0
|
```html run untrusted height=0
|
||||||
<div id="elem"></div>
|
<div id="elem"></div>
|
||||||
|
@ -36,8 +39,6 @@ That's unless we declare the same-named variable by our own:
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
The behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), but it is supported mainly for compatibility. The browser tries to help us by mixing namespaces of JS and DOM. Good for very simple scripts, but there may be name conflicts. Also, when we look in JS and don't have HTML in view, it's not obvious where the variable comes from.
|
|
||||||
|
|
||||||
The better alternative is to use a special method `document.getElementById(id)`.
|
The better alternative is to use a special method `document.getElementById(id)`.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
@ -68,11 +69,111 @@ If there are multiple elements with the same `id`, then the behavior of correspo
|
||||||
The method `getElementById` that can be called only on `document` object. It looks for the given `id` in the whole document.
|
The method `getElementById` that can be called only on `document` object. It looks for the given `id` in the whole document.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## querySelectorAll [#querySelectorAll]
|
||||||
|
|
||||||
|
By far, the most versatile method, `elem.querySelectorAll(css)` returns all elements inside `elem` matching the given CSS selector.
|
||||||
|
|
||||||
|
Here we look for all `<li>` elements that are last children:
|
||||||
|
|
||||||
|
```html run
|
||||||
|
<ul>
|
||||||
|
<li>The</li>
|
||||||
|
<li>test</li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li>has</li>
|
||||||
|
<li>passed</li>
|
||||||
|
</ul>
|
||||||
|
<script>
|
||||||
|
*!*
|
||||||
|
let elements = document.querySelectorAll('ul > li:last-child');
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
for (let elem of elements) {
|
||||||
|
alert(elem.innerHTML); // "test", "passed"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
This method is indeed powerful, because any CSS selector can be used.
|
||||||
|
|
||||||
|
```smart header="Can use pseudo-classes as well"
|
||||||
|
Pseudo-classes in the CSS selector like `:hover` and `:active` are also supported. For instance, `document.querySelectorAll(':hover')` will return the collection with elements that the pointer is over now (in nesting order: from the outermost `<html>` to the most nested one).
|
||||||
|
```
|
||||||
|
|
||||||
|
## querySelector [#querySelector]
|
||||||
|
|
||||||
|
The call to `elem.querySelector(css)` returns the first element for the given CSS selector.
|
||||||
|
|
||||||
|
In other words, the result is the same as `elem.querySelectorAll(css)[0]`, but the latter is looking for *all* elements and picking one, while `elem.querySelector` just looks for one. So it's faster and shorter to write.
|
||||||
|
|
||||||
|
## matches
|
||||||
|
|
||||||
|
Previous methods were searching the DOM.
|
||||||
|
|
||||||
|
The [elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`.
|
||||||
|
|
||||||
|
The method comes handy when we are iterating over elements (like in array or something) and trying to filter those that interest us.
|
||||||
|
|
||||||
|
For instance:
|
||||||
|
|
||||||
|
```html run
|
||||||
|
<a href="http://example.com/file.zip">...</a>
|
||||||
|
<a href="http://ya.ru">...</a>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// can be any collection instead of document.body.children
|
||||||
|
for (let elem of document.body.children) {
|
||||||
|
*!*
|
||||||
|
if (elem.matches('a[href$="zip"]')) {
|
||||||
|
*/!*
|
||||||
|
alert("The archive reference: " + elem.href );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## closest
|
||||||
|
|
||||||
|
*Ancestors* of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top.
|
||||||
|
|
||||||
|
The method `elem.closest(css)` looks the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search.
|
||||||
|
|
||||||
|
In other words, the method `closest` goes up from the element and checks each of parents. If it matches the selector, then the search stops, and the ancestor is returned.
|
||||||
|
|
||||||
|
For instance:
|
||||||
|
|
||||||
|
```html run
|
||||||
|
<h1>Contents</h1>
|
||||||
|
|
||||||
|
<div class="contents">
|
||||||
|
<ul class="book">
|
||||||
|
<li class="chapter">Chapter 1</li>
|
||||||
|
<li class="chapter">Chapter 1</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let chapter = document.querySelector('.chapter'); // LI
|
||||||
|
|
||||||
|
alert(chapter.closest('.book')); // UL
|
||||||
|
alert(chapter.closest('.contents')); // DIV
|
||||||
|
|
||||||
|
alert(chapter.closest('h1')); // null (because h1 is not an ancestor)
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
## getElementsBy*
|
## getElementsBy*
|
||||||
|
|
||||||
There are also other methods to look for nodes:
|
There are also other methods to look for nodes by a tag, class, etc.
|
||||||
|
|
||||||
|
Today, they are mostly history, as `querySelector` is more powerful and shorter to write.
|
||||||
|
|
||||||
|
So here we cover them mainly for completeness, while you can still find them in the old scripts.
|
||||||
|
|
||||||
- `elem.getElementsByTagName(tag)` looks for elements with the given tag and returns the collection of them. The `tag` parameter can also be a star `"*"` for "any tags".
|
- `elem.getElementsByTagName(tag)` looks for elements with the given tag and returns the collection of them. The `tag` parameter can also be a star `"*"` for "any tags".
|
||||||
|
- `elem.getElementsByClassName(className)` returns elements that have the given CSS class. Elements may have other classes too.
|
||||||
|
- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. very rarely used.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
```js
|
```js
|
||||||
|
@ -80,8 +181,6 @@ For instance:
|
||||||
let divs = document.getElementsByTagName('div');
|
let divs = document.getElementsByTagName('div');
|
||||||
```
|
```
|
||||||
|
|
||||||
This method is callable in the context of any DOM element.
|
|
||||||
|
|
||||||
Let's find all `input` tags inside the table:
|
Let's find all `input` tags inside the table:
|
||||||
|
|
||||||
```html run height=50
|
```html run height=50
|
||||||
|
@ -138,12 +237,7 @@ document.getElementsByTagName('input')[0].value = 5;
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
There are also other rarely used methods of this kind:
|
Looking for `.article` elements:
|
||||||
|
|
||||||
- `elem.getElementsByClassName(className)` returns elements that have the given CSS class. Elements may have other classes too.
|
|
||||||
- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. Exists for historical reasons, very rarely used, we mention it here only for completeness.
|
|
||||||
|
|
||||||
For instance:
|
|
||||||
|
|
||||||
```html run height=50
|
```html run height=50
|
||||||
<form name="my-form">
|
<form name="my-form">
|
||||||
|
@ -161,105 +255,6 @@ For instance:
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
## querySelectorAll [#querySelectorAll]
|
|
||||||
|
|
||||||
Now goes the heavy artillery.
|
|
||||||
|
|
||||||
The call to `elem.querySelectorAll(css)` returns all elements inside `elem` matching the given CSS selector. That's the most often used and powerful method.
|
|
||||||
|
|
||||||
Here we look for all `<li>` elements that are last children:
|
|
||||||
|
|
||||||
```html run
|
|
||||||
<ul>
|
|
||||||
<li>The</li>
|
|
||||||
<li>test</li>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li>has</li>
|
|
||||||
<li>passed</li>
|
|
||||||
</ul>
|
|
||||||
<script>
|
|
||||||
*!*
|
|
||||||
let elements = document.querySelectorAll('ul > li:last-child');
|
|
||||||
*/!*
|
|
||||||
|
|
||||||
for (let elem of elements) {
|
|
||||||
alert(elem.innerHTML); // "test", "passed"
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
This method is indeed powerful, because any CSS selector can be used.
|
|
||||||
|
|
||||||
```smart header="Can use pseudo-classes as well"
|
|
||||||
Pseudo-classes in the CSS selector like `:hover` and `:active` are also supported. For instance, `document.querySelectorAll(':hover')` will return the collection with elements that the pointer is over now (in nesting order: from the outermost `<html>` to the most nested one).
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## querySelector [#querySelector]
|
|
||||||
|
|
||||||
The call to `elem.querySelector(css)` returns the first element for the given CSS selector.
|
|
||||||
|
|
||||||
In other words, the result is the same as `elem.querySelectorAll(css)[0]`, but the latter is looking for *all* elements and picking one, while `elem.querySelector` just looks for one. So it's faster and shorter to write.
|
|
||||||
|
|
||||||
## matches
|
|
||||||
|
|
||||||
Previous methods were searching the DOM.
|
|
||||||
|
|
||||||
The [elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`.
|
|
||||||
|
|
||||||
The method comes handy when we are iterating over elements (like in array or something) and trying to filter those that interest us.
|
|
||||||
|
|
||||||
For instance:
|
|
||||||
|
|
||||||
```html run
|
|
||||||
<a href="http://example.com/file.zip">...</a>
|
|
||||||
<a href="http://ya.ru">...</a>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// can be any collection instead of document.body.children
|
|
||||||
for (let elem of document.body.children) {
|
|
||||||
*!*
|
|
||||||
if (elem.matches('a[href$="zip"]')) {
|
|
||||||
*/!*
|
|
||||||
alert("The archive reference: " + elem.href );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## closest
|
|
||||||
|
|
||||||
All elements that are directly above the given one are called its *ancestors*.
|
|
||||||
|
|
||||||
In other words, ancestors are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top.
|
|
||||||
|
|
||||||
The method `elem.closest(css)` looks the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search.
|
|
||||||
|
|
||||||
In other words, the method `closest` goes up from the element and checks each of parents. If it matches the selector, then the search stops, and the ancestor is returned.
|
|
||||||
|
|
||||||
For instance:
|
|
||||||
|
|
||||||
```html run
|
|
||||||
<h1>Contents</h1>
|
|
||||||
|
|
||||||
<div class="contents">
|
|
||||||
<ul class="book">
|
|
||||||
<li class="chapter">Chapter 1</li>
|
|
||||||
<li class="chapter">Chapter 1</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
let chapter = document.querySelector('.chapter'); // LI
|
|
||||||
|
|
||||||
alert(chapter.closest('.book')); // UL
|
|
||||||
alert(chapter.closest('.contents')); // DIV
|
|
||||||
|
|
||||||
alert(chapter.closest('h1')); // null (because h1 is not an ancestor)
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Live collections
|
## Live collections
|
||||||
|
|
||||||
All methods `"getElementsBy*"` return a *live* collection. Such collections always reflect the current state of the document and "auto-update" when it changes.
|
All methods `"getElementsBy*"` return a *live* collection. Such collections always reflect the current state of the document and "auto-update" when it changes.
|
||||||
|
@ -327,6 +322,18 @@ There are 6 main methods to search for nodes in DOM:
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td><code>querySelector</code></td>
|
||||||
|
<td>CSS-selector</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>querySelectorAll</code></td>
|
||||||
|
<td>CSS-selector</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>-</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td><code>getElementById</code></td>
|
<td><code>getElementById</code></td>
|
||||||
<td><code>id</code></td>
|
<td><code>id</code></td>
|
||||||
<td>-</td>
|
<td>-</td>
|
||||||
|
@ -350,24 +357,10 @@ There are 6 main methods to search for nodes in DOM:
|
||||||
<td>✔</td>
|
<td>✔</td>
|
||||||
<td>✔</td>
|
<td>✔</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td><code>querySelector</code></td>
|
|
||||||
<td>CSS-selector</td>
|
|
||||||
<td>✔</td>
|
|
||||||
<td>-</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>querySelectorAll</code></td>
|
|
||||||
<td>CSS-selector</td>
|
|
||||||
<td>✔</td>
|
|
||||||
<td>-</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
Please note that methods `getElementById` and `getElementsByName` can only be called in the context of the document: `document.getElementById(...)`. But not on an element: `elem.getElementById(...)` would cause an error.
|
By far the most used are `querySelector` and `querySelectorAll`, but `getElementBy*` can be sporadically helpful or found in the old scripts.
|
||||||
|
|
||||||
Other methods can be called on elements too. For instance `elem.querySelectorAll(...)` will search inside `elem` (in the DOM subtree).
|
|
||||||
|
|
||||||
Besides that:
|
Besides that:
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue