Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ for (let li of document.querySelectorAll('li')) {
}
```

In the loop we need to get the text inside every `li`. We can read it directly from the first child node, that is the text node:
In the loop we need to get the text inside every `li`.

We can read the text from the first child node of `li`, that is the text node:

```js
for (let li of document.querySelectorAll('li')) {
Expand All @@ -16,4 +18,4 @@ for (let li of document.querySelectorAll('li')) {
}
```

Then we can get the number of descendants `li.getElementsByTagName('li')`.
Then we can get the number of descendants as `li.getElementsByTagName('li').length`.
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ Also, there's a reference to the constructor function inside the `prototype`:
alert(HTMLDocument.prototype.constructor === HTMLDocument); // true
```

For built-in classes in all prototypes there's a `constructor` reference, and we can get `constructor.name` to see the name of the class. Let's do it for all objects in the `document` prototype chain:
To get a name of the class as a string, we can use `constructor.name`. Let's do it for the whole `document` prototype chain, till class `Node`:

```js run
alert(HTMLDocument.prototype.constructor.name); // HTMLDocument
alert(HTMLDocument.prototype.__proto__.constructor.name); // Document
alert(HTMLDocument.prototype.__proto__.__proto__.constructor.name); // Node
```

That's the hierarchy.

We also could examine the object using `console.dir(document)` and see these names by opening `__proto__`. The console takes them from `constructor` internally.
94 changes: 56 additions & 38 deletions 2-ui/1-document/05-basic-dom-node-properties/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,57 @@

Let's get a more in-depth look at DOM nodes.

In this chapter we'll see more into what they are and their most used properties.
In this chapter we'll see more into what they are and learn their most used properties.

## DOM node classes

DOM nodes have different properties depending on their class. For instance, an element node corresponding to tag `<a>` has link-related properties, and the one corresponding to `<input>` has input-related properties and so on. Text nodes are not the same as element nodes. But there are also common properties and methods between all of them, because all classes of DOM nodes form a single hierarchy.
Different DOM nodes may have different properties. For instance, an element node corresponding to tag `<a>` has link-related properties, and the one corresponding to `<input>` has input-related properties and so on. Text nodes are not the same as element nodes. But there are also common properties and methods between all of them, because all classes of DOM nodes form a single hierarchy.

Each DOM node belongs to the corresponding built-in class.

The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](http://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it.
The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](https://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it.

Here's the picture, explanations to follow:

![](dom-class-hierarchy.svg)

The classes are:

- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class. Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later.
- [Node](http://dom.spec.whatwg.org/#interface-node) -- is also an "abstract" class, serving as a base for DOM nodes. It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are concrete node classes that inherit from it, namely: `Text` for text nodes, `Element` for element nodes and more exotic ones like `Comment` for comment nodes.
- [Element](http://dom.spec.whatwg.org/#interface-element) -- is a base class for DOM elements. It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`. A browser supports not only HTML, but also XML and SVG. The `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` and `HTMLElement`.
- [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) -- is finally the basic class for all HTML elements. It is inherited by concrete HTML elements:
- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class for everything.

Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later.

- [Node](https://dom.spec.whatwg.org/#interface-node) -- is also an "abstract" class, serving as a base for DOM nodes.

It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are other classes that inherit from it (and so inherit the `Node` functionality).

- [Document](https://dom.spec.whatwg.org/#interface-document), for historical reasons often inherited by `HTMLDocument` (though the latest spec doesn't dictate it) -- is a document as a whole.

The `document` global object belongs exactly to this class. It serves as an entry point to the DOM.

- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- an "abstract" class, inherited by:
- [Text](https://dom.spec.whatwg.org/#interface-text) -- the class corresponding to a text inside elements, e.g. `Hello` in `<p>Hello</p>`.
- [Comment](https://dom.spec.whatwg.org/#interface-comment) -- the class for comments. They are not shown, but each comment becomes a member of DOM.

- [Element](https://dom.spec.whatwg.org/#interface-element) -- is the base class for DOM elements.

It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`.

A browser supports not only HTML, but also XML and SVG. So the `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` (we don't need them here) and `HTMLElement`.

- Finally, [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) is the basic class for all HTML elements. We'll work with it most of the time.

It is inherited by concrete HTML elements:
- [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- the class for `<input>` elements,
- [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- the class for `<body>` elements,
- [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- the class for `<a>` elements,
- ...and so on.

There are many other tags with their own classes that may have specific properties and methods, while some elements, such as `<span>`, `<section>`, `<article>` do not have any specific properties, so they are instances of `HTMLElement` class.

So, the full set of properties and methods of a given node comes as the result of the inheritance.
So, the full set of properties and methods of a given node comes as the result of the chain of inheritance.

For example, let's consider the DOM object for an `<input>` element. It belongs to [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) class. It gets properties and methods as a superposition of:
For example, let's consider the DOM object for an `<input>` element. It belongs to [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) class.

It gets properties and methods as a superposition of (listed in inheritance order):

Expand All @@ -40,9 +61,9 @@ It gets properties and methods as a superposition of (listed in inheritance orde
- `Element` -- provides generic element methods,
- `Node` -- provides common DOM node properties,
- `EventTarget` -- gives the support for events (to be covered),
- ...and finally it inherits from `Object`, so "pure object" methods like `hasOwnProperty` are also available.
- ...and finally it inherits from `Object`, so "plain object" methods like `hasOwnProperty` are also available.

To see the DOM node class name, we can recall that an object usually has the `constructor` property. It references to the class constructor, and `constructor.name` is its name:
To see the DOM node class name, we can recall that an object usually has the `constructor` property. It references the class constructor, and `constructor.name` is its name:

```js run
alert( document.body.constructor.name ); // HTMLBodyElement
Expand Down Expand Up @@ -80,7 +101,7 @@ Try it on `document.body`.
```

````smart header="IDL in the spec"
In the specification, classes are described not using JavaScript, but a special [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL), that is usually easy to understand.
In the specification, DOM classes aren't described by using JavaScript, but a special [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL), that is usually easy to understand.

In IDL all properties are prepended with their types. For instance, `DOMString`, `boolean` and so on.

Expand All @@ -95,7 +116,7 @@ interface HTMLInputElement: HTMLElement {
// here go properties and methods of <input> elements

*!*
// "DOMString" means that the value of these properties are strings
// "DOMString" means that the value of a property is a string
*/!*
attribute DOMString accept;
attribute DOMString alt;
Expand All @@ -114,13 +135,11 @@ interface HTMLInputElement: HTMLElement {
...
}
```

Other classes are somewhat similar.
````

## The "nodeType" property

The `nodeType` property provides an old-fashioned way to get the "type" of a DOM node.
The `nodeType` property provides one more, "old-fashioned" way to get the "type" of a DOM node.

It has a numeric value:
- `elem.nodeType == 1` for element nodes,
Expand All @@ -135,10 +154,10 @@ For instance:
<script>
let elem = document.body;

// let's examine what it is?
// let's examine: what type of node is in elem?
alert(elem.nodeType); // 1 => element

// and the first child is...
// and its first child is...
alert(elem.firstChild.nodeType); // 3 => text

// for the document object, the type is 9
Expand All @@ -160,7 +179,7 @@ alert( document.body.nodeName ); // BODY
alert( document.body.tagName ); // BODY
```

Is there any difference between tagName and nodeName?
Is there any difference between `tagName` and `nodeName`?

Sure, the difference is reflected in their names, but is indeed a bit subtle.

Expand All @@ -179,18 +198,17 @@ For instance, let's compare `tagName` and `nodeName` for the `document` and a co

<script>
// for comment
alert( document.body.firstChild.tagName ); // undefined (no element)
alert( document.body.firstChild.tagName ); // undefined (not an element)
alert( document.body.firstChild.nodeName ); // #comment

// for document
alert( document.tagName ); // undefined (not element)
alert( document.tagName ); // undefined (not an element)
alert( document.nodeName ); // #document
</script>
</body>
```

If we only deal with elements, then `tagName` is the only thing we should use.

If we only deal with elements, then we can use both `tagName` and `nodeName` - there's no difference.

```smart header="The tag name is always uppercase except in XML mode"
The browser has two modes of processing documents: HTML and XML. Usually the HTML-mode is used for webpages. XML-mode is enabled when the browser receives an XML-document with the header: `Content-Type: application/xml+xhtml`.
Expand All @@ -205,7 +223,7 @@ In XML mode the case is kept "as is". Nowadays XML mode is rarely used.

The [innerHTML](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) property allows to get the HTML inside the element as a string.

We can also modify it. So it's one of most powerful ways to change the page.
We can also modify it. So it's one of the most powerful ways to change the page.

The example shows the contents of `document.body` and then replaces it completely:

Expand Down Expand Up @@ -236,14 +254,12 @@ We can try to insert invalid HTML, the browser will fix our errors:
```

```smart header="Scripts don't execute"
If `innerHTML` inserts a `<script>` tag into the document -- it doesn't execute.

It becomes a part of HTML, just as a script that has already run.
If `innerHTML` inserts a `<script>` tag into the document -- it becomes a part of HTML, but doesn't execute.
```

### Beware: "innerHTML+=" does a full overwrite

We can append "more HTML" by using `elem.innerHTML+="something"`.
We can append HTML to an element by using `elem.innerHTML+="more html"`.

Like this:

Expand Down Expand Up @@ -306,12 +322,12 @@ Consider the example:
*!*
// replace div.outerHTML with <p>...</p>
*/!*
div.outerHTML = '<p>A new element!</p>'; // (*)
div.outerHTML = '<p>A new element</p>'; // (*)

*!*
// Wow! 'div' is still the same!
*/!*
alert(div.outerHTML); // <div>Hello, world!</div>
alert(div.outerHTML); // <div>Hello, world!</div> (**)
</script>
```

Expand All @@ -326,17 +342,17 @@ So what happened in `div.outerHTML=...` is:
- Another piece of HTML `<p>A new element</p>` was inserted in its place.
- `div` still has its old value. The new HTML wasn't saved to any variable.

That's possible with `innerHTML`, but not with `outerHTML`.
It's so easy to make an error here: modify `div.outerHTML` and then continue to work with `div` as if it had the new content in it. But it doesn't. Such thing is correct for `innerHTML`, but not for `outerHTML`.

We can write to `elem.outerHTML`, but should keep in mind that it doesn't change the element we're writing to ('elem'). It puts the new HTML in its place instead. We can get references to the new elements by querying the DOM.

## nodeValue/data: text node content

The `innerHTML` property is only valid for element nodes.

Other node types have their counterpart: `nodeValue` and `data` properties. These two are almost the same for practical use, there are only minor specification differences. So we'll use `data`, because it's shorter.
Other node types, such as text nodes, have their counterpart: `nodeValue` and `data` properties. These two are almost the same for practical use, there are only minor specification differences. So we'll use `data`, because it's shorter.

We can read it, like this:
An example of reading the content of a text node and a comment:

```html run height="50"
<body>
Expand All @@ -356,15 +372,17 @@ We can read it, like this:
</body>
```

For text nodes we can imagine a reason to read or modify them, but why comments? Usually, they are not interesting at all, but sometimes developers embed information into HTML in them, like this:
For text nodes we can imagine a reason to read or modify them, but why comments?

Sometimes developers embed information or template instructions into HTML in them, like this:

```html
<!-- if isAdmin -->
<div>Welcome, Admin!</div>
<!-- /if -->
```

...Then JavaScript can read it and process embedded instructions.
...Then JavaScript can read it from `data` property and process embedded instructions.

## textContent: pure text

Expand Down Expand Up @@ -447,7 +465,7 @@ Here's a blinking element:

## More properties

DOM elements also have additional properties, many of them provided by the class:
DOM elements also have additional properties, in particular those that depend on the class:

- `value` -- the value for `<input>`, `<select>` and `<textarea>` (`HTMLInputElement`, `HTMLSelectElement`...).
- `href` -- the "href" for `<a href="...">` (`HTMLAnchorElement`).
Expand All @@ -468,7 +486,7 @@ For instance:

Most standard HTML attributes have the corresponding DOM property, and we can access it like that.

If we want to know the full list of supported properties for a given class, we can find them in the specification. For instance, HTMLInputElement is documented at <https://html.spec.whatwg.org/#htmlinputelement>.
If we want to know the full list of supported properties for a given class, we can find them in the specification. For instance, `HTMLInputElement` is documented at <https://html.spec.whatwg.org/#htmlinputelement>.

Or if we'd like to get them fast or are interested in a concrete browser specification -- we can always output the element using `console.dir(elem)` and read the properties. Or explore "DOM properties" in the Elements tab of the browser developer tools.

Expand All @@ -494,7 +512,7 @@ Main DOM node properties are:
: The content of a non-element node (text, comment). These two are almost the same, usually we use `data`. Can be modified.

`textContent`
: The text inside the element, basically HTML minus all `<tags>`. Writing into it puts the text inside the element, with all special characters and tags treated exactly as text. Can safely insert user-generated text and protect from unwanted HTML insertions.
: The text inside the element: HTML minus all `<tags>`. Writing into it puts the text inside the element, with all special characters and tags treated exactly as text. Can safely insert user-generated text and protect from unwanted HTML insertions.

`hidden`
: When set to `true`, does the same as CSS `display:none`.
Expand Down
Loading