minor
This commit is contained in:
parent
8360ebbe90
commit
dbf5c7587c
31 changed files with 635 additions and 455 deletions
|
@ -7,13 +7,13 @@ libs:
|
|||
|
||||
# DOM Navigation
|
||||
|
||||
DOM allows to do anything with HTML-element and its contents, but first we need to reach it.
|
||||
DOM allows to do anything with elements and their contents, but first we need to reach the corresponding DOM object, get it into a variable, and then we are able to modify it.
|
||||
|
||||
All operations on DOM start with the `document` object. From it we can access any node.
|
||||
|
||||
[cut]
|
||||
|
||||
Here's a picture of links that allow to walk between DOM nodes:
|
||||
Here's a picture of links that allow to travel between DOM nodes:
|
||||
|
||||

|
||||
|
||||
|
@ -35,9 +35,9 @@ The topmost tree nodes are available directly as `document` properties:
|
|||
````warn header="There's a catch: `document.body` can be `null`"
|
||||
A script cannot access an element that doesn't exist at the moment of running.
|
||||
|
||||
In particular, if a script is inside `<head>`, then `document.body` is unavailable, because the browser did not reach it yet.
|
||||
In particular, if a script is inside `<head>`, then `document.body` is unavailable, because the browser did not read it yet.
|
||||
|
||||
So, in the example below `alert` will show `null`:
|
||||
So, in the example below the first `alert` shows `null`:
|
||||
|
||||
```html run
|
||||
<html>
|
||||
|
@ -61,18 +61,36 @@ So, in the example below `alert` will show `null`:
|
|||
```
|
||||
````
|
||||
|
||||
```smart header="In the DOM world `null` is actively used"
|
||||
In the DOM, the value meaning "no such node" is `null`, not `undefined`.
|
||||
```smart header="In the DOM world `null` means \"doesn't exist\""
|
||||
In the DOM, the `null` value means "doesn't exist" or "no such node".
|
||||
```
|
||||
|
||||
## Children: childNodes, firstChild, lastChild
|
||||
|
||||
There are two tree terms that we'll use:
|
||||
There are two terms that we'll use from now on:
|
||||
|
||||
- **Child nodes (or children)** -- elements that are nested exactly 1 level below the given one. For instance, `<head>` and `<body>` are children of `<html>` element.
|
||||
- **Descendants** -- all elements that are nested in the given one, including children, their children and so on. That is: the DOM subtree.
|
||||
- **Child nodes (or children)** -- elements that are direct children. In other words, they are nested exactly in the given one. For instance, `<head>` and `<body>` are children of `<html>` element.
|
||||
- **Descendants** -- all elements that are nested in the given one, including children, their children and so on.
|
||||
|
||||
The `childNodes` collection provides access to all child nodes, including text nodes.
|
||||
For instance, here `<body>` has children `<div>` and `<ul>` (and few blank text nodes):
|
||||
|
||||
```html run
|
||||
<html>
|
||||
<body>
|
||||
<div>Begin</div>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<b>Information</b>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
...And if we ask for all descendants of `<body>`, then we get direct children `<div>`, `<ul>` and also more nested elements like `<li>` (being a child of `<ul>`) and `<b>` (being a child of `<li>`) -- the whole subtree.
|
||||
|
||||
**The `childNodes` collection provides access to all child nodes, including text nodes.**
|
||||
|
||||
The example below shows children of `document.body`:
|
||||
|
||||
|
@ -99,17 +117,17 @@ The example below shows children of `document.body`:
|
|||
</html>
|
||||
```
|
||||
|
||||
Please note an interesting detail here. If we run the example above, the last element shown is `<script>`. In fact, the document has more stuff below, but at the moment of the execution the browser did not read it yet, so the script doesn't see it.
|
||||
Please note an interesting detail here. If we run the example above, the last element shown is `<script>`. In fact, the document has more stuff below, but at the moment of the script execution the browser did not read it yet, so the script doesn't see it.
|
||||
|
||||
Properties `firstChild` and `lastChild` give fast access to the first and last elements.
|
||||
**Properties `firstChild` and `lastChild` give fast access to the first and last children.**
|
||||
|
||||
If there are child nodes, then the following is always true:
|
||||
They are just shorthands. If there exist child nodes, then the following is always true:
|
||||
```js
|
||||
elem.childNodes[0] === elem.firstChild
|
||||
elem.childNodes[elem.childNodes.length - 1] === elem.lastChild
|
||||
```
|
||||
|
||||
There's also a special function `elem.hasChildNodes()` to check whether an element is empty.
|
||||
There's also a special function `elem.hasChildNodes()` to check whether there are any child nodes.
|
||||
|
||||
### DOM collections
|
||||
|
||||
|
@ -120,66 +138,83 @@ There are two important consequences:
|
|||
1. We can use `for..of` to iterate over it:
|
||||
```js
|
||||
for (let node of document.body.childNodes) {
|
||||
alert(node); // will work the same as above
|
||||
alert(node); // shows all nodes from the collection
|
||||
}
|
||||
```
|
||||
That's because it's iterable -- supports the `Symbol.iterator` property, as required.
|
||||
That's because it's iterable (provides the `Symbol.iterator` property, as required).
|
||||
|
||||
2. Array methods won't work, because it's not an array:
|
||||
```js run
|
||||
alert(document.body.childNodes.filter); // undefined (there's no filter method!)
|
||||
```
|
||||
|
||||
The first thing is nice. The second is tolerable, because we can use `Array.from` to create a "real" array from it:
|
||||
The first thing is nice. The second is tolerable, because we can use `Array.from` to create a "real" array from the collection, if we want array methods:
|
||||
|
||||
```js run
|
||||
alert( Array.from(document.body.childNodes).filter ); // now it's there
|
||||
```
|
||||
|
||||
```warn header="DOM collections are read-only"
|
||||
Even more -- *all* navigation properties listed in this chapter are read-only.
|
||||
DOM collections, and even more -- *all* navigation properties listed in this chapter are read-only.
|
||||
|
||||
We can't replace an element by assigning `childNodes[i] = ...`.
|
||||
We can't replace an child by something else assigning `childNodes[i] = ...`.
|
||||
|
||||
Changing DOM is accomplished with other methods that we'll see in the next chapter.
|
||||
Changing DOM needs other methods, we'll see them in the next chapter.
|
||||
```
|
||||
|
||||
```warn header="DOM collections are live"
|
||||
Almost all DOM collections with minor exceptions are *live*. In other words, they reflect the current state of DOM.
|
||||
|
||||
If new elements appear or get removed from DOM, they are refreshed automatically.
|
||||
If we keep a reference to `elem.childNodes`, and add/remove nodes into DOM, then they appear in the collection automatically.
|
||||
```
|
||||
|
||||
```warn header="Shouldn't use `for..in` to loop over collections"
|
||||
````warn header="Don't use `for..in` to loop over collections"
|
||||
Collections are iterable using `for..of`. Sometimes people try to use `for..in` for that.
|
||||
|
||||
Please, don't. The `for..in` loop iterates over all enumerable properties. And collections some that we usually do not want to get.
|
||||
```
|
||||
Please, don't. The `for..in` loop iterates over all enumerable properties. And collections have some "extra" rarely used properties that we usually do not want to get:
|
||||
|
||||
```html run
|
||||
<body>
|
||||
<script>
|
||||
// shows 0, 1, length, item, values and more.
|
||||
for(let prop in document.body.childNodes) alert(prop);
|
||||
</script>
|
||||
</body>
|
||||
````
|
||||
|
||||
## Siblings and the parent
|
||||
|
||||
The parent is available as `parentNode`. The next node in the same parent (sibling) is `nextSibling`, and the previous one is `previousSibling`.
|
||||
*Siblings* are nodes that are children of the same parent. For instance, `<head>` and `<body>` are siblings:
|
||||
|
||||
- `<body>` is said to be the "next" or "right" sibling of `<head>`,
|
||||
- `<head>` is said to be the "previous" or "left" sibling of `<body>`.
|
||||
|
||||
The parent is available as `parentNode`.
|
||||
|
||||
The next node in the same parent (next sibling) is `nextSibling`, and the previous one is `previousSibling`.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
// <html><head>...</head><body>...</body></html>
|
||||
```html run
|
||||
<html><head></head><body><script>
|
||||
// HTML is "dense" to evade extra "blank" text nodes.
|
||||
|
||||
// parent of <body> is <html>
|
||||
alert( document.body.parentNode === document.documentElement ); // true
|
||||
// parent of <body> is <html>
|
||||
alert( document.body.parentNode === document.documentElement ); // true
|
||||
|
||||
// after <head> goes <body>
|
||||
alert( document.head.nextSibling ); // HTMLBodyElement
|
||||
// after <head> goes <body>
|
||||
alert( document.head.nextSibling ); // HTMLBodyElement
|
||||
|
||||
// before <body> goes <head>
|
||||
alert( document.body.previousSibling ); // HTMLHeadElement
|
||||
// before <body> goes <head>
|
||||
alert( document.body.previousSibling ); // HTMLHeadElement
|
||||
</script></body></html>
|
||||
```
|
||||
|
||||
## Element-only navigation
|
||||
|
||||
Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if there exist.
|
||||
|
||||
But for many tasks we don't want text or comment nodes. We want to manipulate element nodes that represent tags and structure of the page.
|
||||
But for many tasks we don't want text or comment nodes. We want to manipulate element nodes that represent tags and form the structure of the page.
|
||||
|
||||
So let's see more navigation links that only take *element nodes* into account:
|
||||
|
||||
|
@ -193,19 +228,21 @@ The links are similar to those given above, just with `Element` word inside:
|
|||
- `parentElement` -- parent element.
|
||||
|
||||
````smart header="Why `parentElement`? Can the parent be *not* an element?"
|
||||
The `parentElement` property returns the element parent, while `parentNode` returns "any node" parent. These properties are always the same, with one exception:
|
||||
The `parentElement` property returns the "element" parent, while `parentNode` returns "any node" parent. These properties are usually the same: they both get the parent.
|
||||
|
||||
With the one exception of `document.documentElement`:
|
||||
|
||||
```js run
|
||||
alert( document.documentElement.parentNode ); // document
|
||||
alert( document.documentElement.parentElement ); // null
|
||||
```
|
||||
|
||||
In other words, the `documentElement` (`<html>`) is the root node. Formally, it has `document` as its parent. But `document` is not an element, so `parentNode` returns it and `parentElement` does not.
|
||||
In other words, the `documentElement` (`<html>`) is the root node. Formally, it has `document` as its parent. But `document` is not an element node, so `parentNode` returns it and `parentElement` does not.
|
||||
|
||||
Sometimes that matters when we're walking over the chain of parents and call a method on each of them, but `document` doesn't have it, so we don't want it.
|
||||
Sometimes that matters when we're walking over the chain of parents and call a method on each of them, but `document` doesn't have it, so we exclude it.
|
||||
````
|
||||
|
||||
Let's modify one of examples above: replace `childNodes` with `children`. Now it will show only elements:
|
||||
Let's modify one of examples above: replace `childNodes` with `children`. Now it shows only elements:
|
||||
|
||||
```html run
|
||||
<html>
|
||||
|
@ -232,17 +269,19 @@ Let's modify one of examples above: replace `childNodes` with `children`. Now it
|
|||
|
||||
## More links: tables [#dom-navigation-tables]
|
||||
|
||||
Till now we described the basic navigation properties. Certain types of DOM elements may provide more, specific to their type, for better convenience.
|
||||
Till now we described the basic navigation properties.
|
||||
|
||||
Certain types of DOM elements may provide additional properties, specific to their type, for convenience.
|
||||
|
||||
Tables are a great example and important particular case of that.
|
||||
|
||||
**`<table>`** elements support these properties:
|
||||
**`<table>`** element supports (in addition to the given above) these properties:
|
||||
- `table.rows` -- the collection of `<tr>` elements of the table.
|
||||
- `table.caption/tHead/tFoot` -- references to elements `<caption>`, `<thead>`, `<tfoot>`.
|
||||
- `table.tBodies` -- the collection of `<tbody>` elements (can be many according to the standard).
|
||||
|
||||
**`<thead>`, `<tfoot>`, `<tbody>`** elements provide:
|
||||
- `tbody.rows` -- the collection of `<tr>` inside them.
|
||||
**`<thead>`, `<tfoot>`, `<tbody>`** elements provide the `rows` property:
|
||||
- `tbody.rows` -- the collection of `<tr>` inside.
|
||||
|
||||
**`<tr>`:**
|
||||
- `tr.cells` -- the collection of `<td>` and `<th>` cells inside the given `<tr>`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue