This commit is contained in:
Ilya Kantor 2019-08-05 00:57:55 +03:00
parent 6d1fa5de73
commit d63c27bcc6
7 changed files with 108 additions and 83 deletions

View file

@ -8,15 +8,25 @@ libs:
The backbone of an HTML document are tags.
According to Document Object Model (DOM), every HTML-tag is an object. Nested tags are called "children" of the enclosing one.
According to Document Object Model (DOM), every HTML-tag is an object. Nested tags are "children" of the enclosing one. The text inside a tag it is an object as well.
The text inside a tag it is an object as well.
All these objects are accessible using JavaScript, we can use them to modify the page.
All these objects are accessible using JavaScript.
For example, `document.body` is the object representing `<body>` tag.
Running this code will make the `<body>` red for 3 seconds:
```js run
document.body.style.background = 'red'; // make the background red
setTimeout(() => document.body.style.background = '', 3000); // return back
```
That was just a glimpse of DOM power. Soon we'll learn more ways to manipulate DOM, but first we need to know about its structure.
## An example of DOM
For instance, let's explore the DOM for this document:
Let's start with the following simple docment:
```html run no-beautify
<!DOCTYPE HTML>
@ -44,7 +54,9 @@ drawHtmlTree(node1, 'div.domtree', 690, 320);
On the picture above, you can click on element nodes and their children will open/collapse.
```
Tags are called *element nodes* (or just elements). Nested tags become children of the enclosing ones. As a result we have a tree of elements: `<html>` is at the root, then `<head>` and `<body>` are its children, etc.
Every tree node is an object.
Tags are *element nodes* (or just elements), they form the tree structure: `<html>` is at the root, then `<head>` and `<body>` are its children, etc.
The text inside elements forms *text nodes*, labelled as `#text`. A text node contains only a string. It may not have children and is always a leaf of the tree.
@ -55,7 +67,7 @@ Please note the special characters in text nodes:
- a newline: `↵` (in JavaScript known as `\n`)
- a space: `␣`
Spaces and newlines -- are totally valid characters, they form text nodes and become a part of the DOM. So, for instance, in the example above the `<head>` tag contains some spaces before `<title>`, and that text becomes a `#text` node (it contains a newline and some spaces only).
Spaces and newlines -- are totally valid characters, like letters and digits. They form text nodes and become a part of the DOM. So, for instance, in the example above the `<head>` tag contains some spaces before `<title>`, and that text becomes a `#text` node (it contains a newline and some spaces only).
There are only two top-level exclusions:
1. Spaces and newlines before `<head>` are ignored for historical reasons,
@ -78,15 +90,14 @@ let node2 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1,
drawHtmlTree(node2, 'div.domtree', 690, 210);
</script>
```smart header="Edge spaces and in-between empty text are usually hidden in tools"
```smart header="Spaces at string start/end and space-only text nodes are usually hidden in tools"
Browser tools (to be covered soon) that work with DOM usually do not show spaces at the start/end of the text and empty text nodes (line-breaks) between tags.
That's because they are mainly used to decorate HTML, and do not affect how it is shown (in most cases).
Developer tools save screen space this way.
On further DOM pictures we'll sometimes omit them where they are irrelevant, to keep things short.
On further DOM pictures we'll sometimes omit them when they are irrelevant. Such spaces usually do not affect how the document is displayed.
```
## Autocorrection
If the browser encounters malformed HTML, it automatically corrects it when making DOM.
@ -148,7 +159,9 @@ You see? The `<tbody>` appeared out of nowhere. You should keep this in mind whi
## Other node types
Let's add more tags and a comment to the page:
There are some other node types besides elements and text nodes.
For example, comments:
```html
<!DOCTYPE HTML>
@ -174,7 +187,7 @@ let node6 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1,
drawHtmlTree(node6, 'div.domtree', 690, 500);
</script>
Here we see a new tree node type -- *comment node*, labeled as `#comment`.
We can see here a new tree node type -- *comment node*, labeled as `#comment`, between two text nodes.
We may think -- why is a comment added to the DOM? It doesn't affect the visual representation in any way. But there's a rule -- if something's in HTML, then it also must be in the DOM tree.
@ -195,8 +208,6 @@ There are [12 node types](https://dom.spec.whatwg.org/#node). In practice we usu
To see the DOM structure in real-time, try [Live DOM Viewer](http://software.hixie.ch/utilities/js/live-dom-viewer/). Just type in the document, and it will show up DOM at an instant.
## In the browser inspector
Another way to explore the DOM is to use the browser developer tools. Actually, that's what we use when developing.
To do so, open the web-page [elks.html](elks.html), turn on the browser developer tools and switch to the Elements tab.
@ -225,10 +236,12 @@ The best way to study them is to click around. Most values are editable in-place
## Interaction with console
As we explore the DOM, we also may want to apply JavaScript to it. Like: get a node and run some code to modify it, to see the result. Here are few tips to travel between the Elements tab and the console.
As we work the DOM, we also may want to apply JavaScript to it. Like: get a node and run some code to modify it, to see the result. Here are few tips to travel between the Elements tab and the console.
- Select the first `<li>` in the Elements tab.
- Press `key:Esc` -- it will open console right below the Elements tab.
For the start:
1. Select the first `<li>` in the Elements tab.
2. Press `key:Esc` -- it will open console right below the Elements tab.
Now the last selected element is available as `$0`, the previously selected is `$1` etc.
@ -236,9 +249,11 @@ We can run commands on them. For instance, `$0.style.background = 'red'` makes t
![](domconsole0.png)
From the other side, if we're in console and have a variable referencing a DOM node, then we can use the command `inspect(node)` to see it in the Elements pane.
That's how to get a node from Elements in Console.
Or we can just output it in the console and explore "at-place", like `document.body` below:
There's also a road back. If there's a variable referencing a DOM node, then we can use the command `inspect(node)` in Console to see it in the Elements pane.
Or we can just output DOM-node in the console and explore "at-place", like `document.body` below:
![](domconsole1.png)

View file

@ -9,7 +9,7 @@ libs:
The DOM allows us to do anything with elements and their contents, but first we need to reach the corresponding DOM object.
All operations on the DOM start with the `document` object. From it we can access any node.
All operations on the DOM start with the `document` object. That's the main "entry point" to DOM. From it we can access any node.
Here's a picture of links that allow for travel between DOM nodes:
@ -86,9 +86,9 @@ For instance, here `<body>` has children `<div>` and `<ul>` (and few blank text
</html>
```
...And all descendants of `<body>` are not only direct children `<div>`, `<ul>` but also more deeply nested elements, such as `<li>` (a child of `<ul>`) and `<b>` (a child of `<li>`) -- the entire subtree.
...And descendants of `<body>` are not only direct children `<div>`, `<ul>` but also more deeply nested elements, such as `<li>` (a child of `<ul>`) and `<b>` (a child of `<li>`) -- the entire subtree.
**The `childNodes` collection provides access to all child nodes, including text nodes.**
**The `childNodes` collection lists all child nodes, including text nodes.**
The example below shows children of `document.body`:
@ -182,30 +182,34 @@ Please, don't. The `for..in` loop iterates over all enumerable properties. And c
## Siblings and the parent
*Siblings* are nodes that are children of the same parent. For instance, `<head>` and `<body>` are siblings:
*Siblings* are nodes that are children of the same parent.
For instance, here `<head>` and `<body>` are siblings:
```html
<html>
<head>...</head><body>...</body>
</html>
```
- `<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 next sibling is is `nextSibling`, and the previous one is `previousSibling`.
The parent is available as `parentNode`.
The next node in the same parent (next sibling) is `nextSibling`, and the previous one is `previousSibling`.
So all these tests are truthy:
For instance:
```js
// parent of <body> is <html>
alert( document.body.parentNode === document.documentElement ); // true
```html run
<html><head></head><body><script>
// HTML is "dense" to evade extra "blank" text nodes.
// after <head> goes <body>
alert( document.head.nextSibling ); // HTMLBodyElement
// parent of <body> is <html>
alert( document.body.parentNode === document.documentElement ); // true
// after <head> goes <body>
alert( document.head.nextSibling ); // HTMLBodyElement
// before <body> goes <head>
alert( document.body.previousSibling ); // HTMLHeadElement
</script></body></html>
// before <body> goes <head>
alert( document.body.previousSibling ); // HTMLHeadElement
```
## Element-only navigation
@ -235,12 +239,12 @@ 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 node, so `parentNode` returns it and `parentElement` does not.
The reason is that root node `document.documentElement` (`<html>`) has `document` as its parent. But `document` is not an element node, so `parentNode` returns it and `parentElement` does not.
This loop travels up from an arbitrary element `elem` to `<html>`, but not to the `document`:
This detail may be useful when we want to travel up from an arbitrary element `elem` to `<html>`, but not to the `document`:
```js
while(elem = elem.parentElement) {
alert( elem ); // parent chain till <html>
while(elem = elem.parentElement) { // go up till <html>
alert( elem );
}
```
````

View file

@ -11,15 +11,15 @@ table.getElementsByTagName('label')
// or
document.querySelectorAll('#age-table label')
// 3. The first td in that table (with the word "Age").
// 3. The first td in that table (with the word "Age")
table.rows[0].cells[0]
// or
table.getElementsByTagName('td')[0]
// or
table.querySelector('td')
// 4. The form with the name "search".
// assuming there's only one element with name="search"
// 4. The form with the name "search"
// assuming there's only one element with name="search" in the document
let form = document.getElementsByName('search')[0]
// or, form specifically
document.querySelector('form[name="search"]')
@ -29,8 +29,7 @@ form.getElementsByTagName('input')[0]
// or
form.querySelector('input')
// 6. The last input in that form.
// there's no direct query for that
let inputs = form.querySelectorAll('input') // search all
inputs[inputs.length-1] // take last
// 6. The last input in that form
let inputs = form.querySelectorAll('input') // find all inputs
inputs[inputs.length-1] // take the last one
```

View file

@ -6,12 +6,12 @@ importance: 4
Here's the document with the table and form.
How to find?
How to find?...
1. The table with `id="age-table"`.
2. All `label` elements inside that table (there should be 3 of them).
3. The first `td` in that table (with the word "Age").
4. The `form` with the name `search`.
4. The `form` with `name="search"`.
5. The first `input` in that form.
6. The last `input` in that form.

View file

@ -105,7 +105,7 @@ Pseudo-classes in the CSS selector like `:hover` and `:active` are also supporte
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.
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 also shorter to write.
## matches