0% found this document useful (0 votes)
94 views52 pages

Understanding the Document Object Model

Chapter 13 discusses the Document Object Model (DOM), which is an API for HTML and XML documents that represents a document as a hierarchical tree of nodes. It covers the structure of the DOM, its interaction with JavaScript, and the Browser Object Model (BOM), highlighting how developers can manipulate web pages and handle browser-specific functionalities. The chapter also explains various node types, the importance of the document object, and provides insights into using developer tools for DOM exploration.

Uploaded by

houssaminfo05
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
94 views52 pages

Understanding the Document Object Model

Chapter 13 discusses the Document Object Model (DOM), which is an API for HTML and XML documents that represents a document as a hierarchical tree of nodes. It covers the structure of the DOM, its interaction with JavaScript, and the Browser Object Model (BOM), highlighting how developers can manipulate web pages and handle browser-specific functionalities. The chapter also explains various node types, the importance of the document object, and provides insights into using developer tools for DOM exploration.

Uploaded by

houssaminfo05
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd

Chapter 13

DOM
Objectives:
 Understanding the DOM as a hierarchy of nodes
 Working with the various node types
 Coding the DOM around browser incompatibilities and gotchas
The Document Object Model (DOM) is an application programming interface (API) for HTML and XML
documents. The DOM represents a document as a hierarchical tree of nodes, allowing developers to add, remove,
and modify individual parts of the page. Evolving out of early Dynamic HTML (DHTML) innovations from
Netscape and Microsoft, the DOM is now a truly cross-platform, language-independent way of representing and
manipulating pages for markup.
DOM Level 1 became a W3C recommendation in October 1998, providing interfaces for basic document
structure and querying. This chapter focuses on the features and uses of DOM Level 1 as it relates to HTML pages
in the browser and its implementation in JavaScript. The most recent versions of Internet Explorer, Firefox, Safari,
Chrome, and Opera all have excellent DOM implementations.

1 Browser Environment

The JavaScript language was initially created for web browsers. Since then it has evolved and become a
language with many uses and platforms. A platform may be a browser, or a web-server or another host, even a
“smart” coffee machine, if it can run JavaScript. Each of them provides platform-specific functionality. The
JavaScript specification calls that a host environment.
A host environment provides own objects and functions additional to the language core. Web browsers give a
means to control web pages. [Link] provides server-side features, and so on. Here’s a bird’s-eye view of what we
have when JavaScript runs in a web browser:

There’s a “root” object called window. It has two roles:


 First, it is a global object for JavaScript code, as described in the chapter Global object.
 Second, it represents the “browser window” and provides methods to control it.
For instance, here we use it as a global object:
function sayHi() {
alert("Hello");
}

// global functions are methods of the global object:


[Link]();
And here we use it as a browser window, to see the window height:
alert([Link]); // inner window height
There are more window-specific methods and properties, we’ll cover them later.

1.1 DOM (Document Object Model)

Document Object Model, or DOM for short, represents all page content as objects that can be modified. The
document object is the main “entry point” to the page. We can change or create anything on the page using it. For
instance:
// change the background color to red
[Link] = "red";

// change it back after 1 second


setTimeout(() => [Link] = "", 1000);
Here we used [Link], but there’s much, much more. Properties and methods are described in the
specification: DOM Living Standard.
DOM is not only for browsers. The DOM specification explains the structure of a document and provides objects
to manipulate it. There are non-browser instruments that use DOM too. For instance, server-side scripts that
download HTML pages and process them can also use DOM. They may support only a part of the specification
though.

1.2 BOM (Browser Object Model)

The Browser Object Model (BOM) represents additional objects provided by the browser (host environment) for
working with everything except the document. For instance:
The navigator object provides background information about the browser and the operating system. There are
many properties, but the two most widely known are: [Link] – about the current browser, and
[Link] – about the platform (can help to differ between Windows/Linux/Mac etc).
The location object allows us to read the current URL and can redirect the browser to a new one. Here’s how we
can use the location object:
alert([Link]); // shows current URL
if (confirm("Go to Wikipedia?")) {
[Link] = "[Link] // redirect the browser to another URL
}
Functions alert/confirm/prompt are also a part of BOM: they are directly not related to the document, but
represent pure browser methods of communicating with the user.
BOM is the part of the general HTML specification. Yes, you heard that right. The HTML spec at
[Link] is not only about the “HTML language” (tags, attributes), but also covers a bunch of
objects, methods and browser-specific DOM extensions. That’s “HTML in broad terms”. Also, some parts have
additional specs listed at [Link]

2 DOM Basics

2.1 DOM Tree

The backbone of an HTML document is tags. According to the Document Object Model (DOM), every HTML
tag is an object. Nested tags are “children” of the enclosing one. The text inside a tag is an object as well. All these
objects are accessible using JavaScript, and we can use them to modify the page. For example, [Link] is
the object representing the <body> tag. Running this code will make the <body> red for 3 seconds:
[Link] = 'red'; // make the background red
setTimeout(() => [Link] = '', 3000); // return back
Here we used [Link] to change the background color of [Link], but there are many other
properties, such as:
 innerHTML – HTML contents of the node.
 offsetWidth – the node width (in pixels)
 …and so on.
Soon we’ll learn more ways to manipulate the DOM, but first we need to know about its structure.

2.1.1 An example of the DOM

Let’s start with the following simple document:


<!DOCTYPE HTML>
<html>
<head>
<title>About elk</title>
</head>
<body>
The truth about elk.
</body>
</html>
The DOM represents HTML as a tree structure of tags. Here’s how it looks:
Every tree node is an object. Tags are element nodes (or just elements) and 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. For instance, the
<title> tag has the text "About elk".

 a newline: ↵ (in JavaScript known as \n)


Please note the special characters in text nodes:

 a space: ␣
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:
 Spaces and newlines before <head> are ignored for historical reasons.
 If we put something after </body>, then that is automatically moved inside the body, at the end, as the HTML
spec requires that all content must be inside <body>. So there can’t be any spaces after </body>.
In other cases everything’s straightforward – if there are spaces (just like any character) in the document, then
they become text nodes in the DOM, and if we remove them, then there won’t be any. Here are no space-only text
nodes:
<!DOCTYPE HTML>
<html><head><title>About elk</title></head><body>The truth about
elk.</body></html>

Spaces at string start/end and space-only text nodes are usually hidden in tool . 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. Developer tools save screen space this way.
On further DOM pictures we’ll sometimes omit them when they are irrelevant. Such spaces usually do not
affect how the document is displayed.

2.1.2 Autocorrection

If the browser encounters malformed HTML, it automatically corrects it when making the DOM. For instance,
the top tag is always <html>. Even if it doesn’t exist in the document, it will exist in the DOM, because the browser
will create it. The same goes for <body>. As an example, if the HTML file is the single word "Hello", the browser
will wrap it into <html> and <body>, and add the required <head>, and the DOM will be:

While generating the DOM, browsers automatically process errors in the document, close tags and so on. A
document with unclosed tags:
<p>Hello
<li>Mom
<li>and
<li>Dad
will become a normal DOM as the browser reads tags and restores the missing parts:

Tables always have <tbody>. An interesting “special case” is tables. By DOM specification they must have
<tbody> tag, but HTML text may omit it. Then the browser creates <tbody> in the DOM automatically. For the
HTML:
<table id="table"><tr><td>1</td></tr></table>
DOM-structure will be:
You see? The <tbody> appeared out of nowhere. We should keep this in mind while working with tables to avoid
surprises.

2.1.3 Other node types

There are some other node types besides elements and text nodes. For example, comments:
<!DOCTYPE HTML>
<html>
<body>
The truth about elk.
<ol>
<li>An elk is a smart</li>
<!-- comment -->
<li>...and cunning animal!</li>
</ol>
</body>
</html>

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. Everything in HTML, even comments,
becomes a part of the DOM. Even the <!DOCTYPE...> directive at the very beginning of HTML is also a DOM
node. It’s in the DOM tree right before <html>. Few people know about that. We are not going to touch that node,
we even don’t draw it on diagrams, but it’s there.
The document object that represents the whole document is, formally, a DOM node as well. There are 12 node
types. In practice we usually work with 4 of them:
 document – the “entry point” into DOM.
 element nodes – HTML-tags, the tree building blocks.
 text nodes – contain text.
 comments – sometimes we can put information there, it won’t be shown, but JS can read it from the DOM.

2.1.4 See it for yourself

To see the DOM structure in real-time, try Live DOM Viewer. Just type in the document, and it will show up as a
DOM at an instant. 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 [Link], turn on the browser developer tools and switch to the
Elements tab. It should look like this:

You can see the DOM, click on elements, see their details and so on. Please note that the DOM structure in
developer tools is simplified. Text nodes are shown just as text. And there are no “blank” (space only) text nodes at
all. That’s fine, because most of the time we are interested in element nodes. Clicking the button in the left-upper
corner allows us to choose a node from the webpage using a mouse (or other pointer devices) and “inspect” it
(scroll to it in the Elements tab). This works great when we have a huge HTML page (and corresponding huge
DOM) and would like to see the place of a particular element in it.
Another way to do it would be just right-clicking on a webpage and selecting “Inspect” in the context menu.
At the right part of the tools there are the following subtabs:
 Styles – we can see CSS applied to the current element rule by rule, including built-in rules (gray). Almost
everything can be edited in-place, including the dimensions/margins/paddings of the box below.
 Computed – to see CSS applied to the element by property: for each property we can see a rule that gives it
(including CSS inheritance and such).
 Event Listeners – to see event listeners attached to DOM elements (we’ll cover them in the next part of the
tutorial).
 …and so on.
The best way to study them is to click around. Most values are editable in-place.

2.1.5 Interaction with 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. For the start:
 Select the first <li> in the Elements tab.
 Press 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. We can run commands on
them. For instance, $[Link] = 'red' makes the selected list item red, like this:

That’s how to get a node from Elements in Console. 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 the DOM node in the console and explore “in-place”, like [Link] below:
That’s for debugging purposes of course. From the next chapter on we’ll access and modify DOM using
JavaScript. The browser developer tools are a great help in development: we can explore the DOM, try things and
see what goes wrong.

2.2 Walk with DOM

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. 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:

Let’s discuss them in more detail.


2.2.1 documentElement and body

The topmost tree nodes are available directly as document properties:


<html> = [Link]
The topmost document node is [Link]. That’s the DOM node of the <html> tag.
<body> = [Link]
Another widely used DOM node is the <body> element – [Link].
<head> = [Link]
The <head> tag is available as [Link].
There’s a catch: [Link] 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 [Link] is unavailable, because the browser did
not read it yet. So, in the example below the first alert shows null:
<html>
<head>
<script>
alert( "From HEAD: " + [Link] ); // null, there's no <body> yet
</script>
</head>

<body>

<script>
alert( "From BODY: " + [Link] ); // HTMLBodyElement, now it exists
</script>

</body>
</html>
In the DOM, the null value means “doesn’t exist” or “no such node”.

2.2.2 childNodes, firstChild, lastChild

There are two terms that we’ll use from now on:
 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.
For instance, here <body> has children <div> and <ul> (and few blank text nodes):
<html>
<body>
<div>Begin</div>
<ul>
<li>
<b>Information</b>
</li>
</ul>
</body>
</html>
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 lists all child nodes, including text nodes. The example below shows children of
[Link]:
<html>
<body>
<div>Begin</div>

<ul>
<li>Information</li>
</ul>

<div>End</div>

<script>
for (let i = 0; i < [Link]; i++) {
alert( [Link][i] ); // Text, DIV, Text, UL, ..., SCRIPT
}
</script>
...more stuff...
</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 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 children. They are just shorthands. If there
exist child nodes, then the following is always true:
[Link][0] === [Link]
[Link][[Link] - 1] === [Link]
There’s also a special function [Link]() to check whether there are any child nodes.

2.2.3 DOM collections

As we can see, childNodes looks like an array. But actually it’s not an array, but rather a collection – a special
array-like iterable object.
There are two important consequences:
1. We can use for..of to iterate over it:
for (let node of [Link]) {
alert(node); // shows all nodes from the collection
}
That’s because it’s iterable (provides the [Link] property, as required).
2. Array methods won’t work, because it’s not an array:
alert([Link]); // undefined (there's no filter method!)
The first thing is nice. The second is tolerable, because we can use [Link] to create a “real” array from the
collection, if we want array methods:
alert( [Link]([Link]).filter ); // function
DOM collections are read-only. DOM collections, and even more – all navigation properties listed in this chapter
are read-only. We can’t replace a child by something else by assigning childNodes[i] = .... Changing DOM needs
other methods. We will see them in the next chapter.
DOM collections are live. Almost all DOM collections with minor exceptions are live. In other words, they
reflect the current state of DOM. If we keep a reference to [Link], and add/remove nodes into DOM, then
they appear in the collection automatically.
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 have some
“extra” rarely used properties that we usually do not want to get:
<body>
<script>
// shows 0, 1, length, item, values and more.
for (let prop in [Link]) alert(prop);
</script>
</body>

2.2.4 Siblings and the parent

Siblings are nodes that are children of the same parent. For instance, here <head> and <body> are siblings:
<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 in nextSibling property, and the previous one – in previousSibling. The parent is available as
parentNode. For example:
// parent of <body> is <html>
alert( [Link] === [Link] ); // true

// after <head> goes <body>


alert( [Link] ); // HTMLBodyElement

// before <body> goes <head>


alert( [Link] ); // HTMLHeadElement

2.2.5 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 they exist. 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:

The links are similar to those given above, just with Element word inside:
 children – only those children that are element nodes.
 firstElementChild, lastElementChild – first and last element children.
 previousElementSibling, nextElementSibling – neighbor elements.
 parentElement – parent element.
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 usually the same: they both get the parent.
With the one exception of [Link]:
alert( [Link] ); // document
alert( [Link] ); // null
The reason is that the root node [Link] (<html>) has document as its parent. But document
is not an element node, so parentNode returns it and parentElement does not. This detail may be useful when we
want to travel up from an arbitrary element elem to <html>, but not to the document:
while(elem = [Link]) { // go up till <html>
alert( elem );
}
Let’s modify one of the examples above: replace childNodes with children. Now it shows only elements:
<html>
<body>
<div>Begin</div>

<ul>
<li>Information</li>
</ul>

<div>End</div>

<script>
for (let elem of [Link]) {
alert(elem); // DIV, UL, DIV, SCRIPT
}
</script>
...
</body>
</html>

2.2.6 More links: tables

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 of that, and represent a particularly
important case:
The <table> element supports (in addition to the given above) these properties:
 [Link] – the collection of <tr> elements of the table.
 [Link]/tHead/tFoot – references to elements <caption>, <thead>, <tfoot>.
 [Link] – the collection of <tbody> elements (can be many according to the standard, but there will
always be at least one – even if it is not in the source HTML, the browser will put it in the DOM).
<thead>, <tfoot>, <tbody> elements provide the rows property:
 [Link] – the collection of <tr> inside.
<tr>:
 [Link] – the collection of <td> and <th> cells inside the given <tr>.
 [Link] – the position (index) of the given <tr> inside the enclosing <thead>/<tbody>/<tfoot>.
 [Link] – the number of the <tr> in the table as a whole (including all table rows).
<td> and <th>:
 [Link] – the number of the cell inside the enclosing <tr>.
An example of usage:
<table id="table">
<tr>
<td>one</td><td>two</td>
</tr>
<tr>
<td>three</td><td>four</td>
</tr>
</table>

<script>
// get td with "two" (first row, second column)
let td = [Link][0].cells[1];
[Link] = "red"; // highlight it
</script>
There are also additional navigation properties for HTML forms. We’ll look at them later when we start working
with forms.
3 Get the Elements

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 6 main methods to search for nodes in DOM:
Method Searches by... Can call on an element? Live?
querySelector CSS-selector ✔ -
querySelectorAll CSS-selector ✔ -
getElementById id - -
getElementsByName name - ✔
getElementsByTagName tag or '*' ✔ ✔
getElementsByClassName class ✔ ✔

3.1 [Link] or just id

If an element has the id attribute, we can get the element using the method [Link](id), no
matter where it is. For instance:
<div id="elem">
<div id="elem-content">Element</div>
</div>

<script>
// get the element
let elem = [Link]('elem');

// make its background red


[Link] = 'red';
</script>
Also, there’s a global variable named by id that references the element:
<div id="elem">
<div id="elem-content">Element</div>
</div>

<script>
// elem is a reference to DOM-element with id="elem"
[Link] = 'red';

// id="elem-content" has a hyphen inside, so it can't be a variable name


// ...but we can access it using square brackets: window['elem-content']
</script>
That’s unless we declare a JavaScript variable with the same name, then it takes precedence:
<div id="elem"></div>
<script>
let elem = 5; // now elem is 5, not a reference to <div id="elem">
alert(elem); // 5
</script>
Please don’t use id-named global variables to access elements. This behavior is described in the specification, so
it’s kind of standard. But it is supported mainly for compatibility. The browser tries to help us by mixing
namespaces of JS and DOM. That’s fine for simple scripts, inlined into HTML, but generally isn’t a good thing.
There may be naming conflicts. Also, when one reads JS code and doesn’t have HTML in view, it’s not obvious
where the variable comes from. Here in the tutorial we use id to directly reference an element for brevity, when it’s
obvious where the element comes from. In real life [Link] is the preferred method.
The id must be unique. There can be only one element in the document with the given id. If there are multiple
elements with the same id, then the behavior of methods that use it is unpredictable, e.g. [Link]
may return any of such elements at random. So please stick to the rule and keep id unique.
The method getElementById can be called only on document object. It looks for the given id in the whole
document.

3.2 querySelectorAll

By far, the most versatile method, [Link](css) returns all elements inside elem matching the
given CSS selector. Here we look for all <li> elements that are last children:
<ul>
<li>The</li>
<li>test</li>
</ul>
<ul>
<li>has</li>
<li>passed</li>
</ul>
<script>
let elements = [Link]('ul > li:last-child');

for (let elem of elements) {


alert([Link]); // "test", "passed"
}
</script>
This method is indeed powerful, because any CSS selector can be used.
Pseudo-classes in the CSS selector like :hover and :active are also supported. For instance,
[Link](':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).

3.3 querySelector

The call to [Link](css) returns the first element for the given CSS selector. In other words, the result
is the same as [Link](css)[0], but the latter is looking for all elements and picking one, while
[Link] just looks for one. So it’s faster and also shorter to write.

3.3.1 matches

Previous methods were searching the DOM. The [Link](css) does not look for anything, it merely checks
if elem matches the given CSS-selector. It returns true or false. The method comes in handy when we are iterating
over elements (like in an array or something) and trying to filter out those that interest us. For instance:
<a href="[Link]
<a href="[Link]

<script>
// can be any collection instead of [Link]
for (let elem of [Link]) {
if ([Link]('a[href$="zip"]')) {
alert("The archive reference: " + [Link] );
}
}
</script>

3.3.2 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 [Link](css) looks for 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:
<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 = [Link]('.chapter'); // LI
alert([Link]('.book')); // UL
alert([Link]('.contents')); // DIV

alert([Link]('h1')); // null (because h1 is not an ancestor)


</script>

3.4 getElementsBy*

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.
 [Link](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”.
 [Link](className) returns elements that have the given CSS class.
 [Link](name) returns elements with the given name attribute, document-wide.
Very rarely used.
For instance:
// get all divs in the document
let divs = [Link]('div');
Let’s find all input tags inside the table:

<table id="table">
<tr>
<td>Your age:</td>

<td>
<label>
<input type="radio" name="age" value="young" checked> less than 18
</label>
<label>
<input type="radio" name="age" value="mature"> from 18 to 50
</label>
<label>
<input type="radio" name="age" value="senior"> more than 60
</label>
</td>
</tr>
</table>

<script>
let inputs = [Link]('input');
for (let input of inputs) {
alert( [Link] + ': ' + [Link] );
}
</script>
Novice developers sometimes forget the letter "s". That is, they try to call getElementByTagName instead of
getElementsByTagName. The "s" letter is absent in getElementById, because it returns a single element. But
getElementsByTagName returns a collection of elements, so there’s "s" inside.
It returns a collection, not an element! Another widespread novice mistake is to write:
// doesn't work
[Link]('input').value = 5;
That won’t work, because it takes a collection of inputs and assigns the value to it rather than to elements inside
it. We should either iterate over the collection or get an element by its index, and then assign, like this:
// should work (if there's an input)
[Link]('input')[0].value = 5;
Looking for .article elements:

<form name="my-form">
<div class="article">Article</div>
<div class="long article">Long article</div>
</form>

<script>
// find by name attribute
let form = [Link]('my-form')[0];

// find by class inside the form


let articles = [Link]('article');
alert([Link]); // 2, found two elements with class "article"
</script>
All methods "getElementsBy*" return a live collection. Such collections always reflect the current state of the
document and “auto-update” when it changes. In the example below, there are two scripts.
 The first one creates a reference to the collection of <div>. As of now, its length is 1.
 The second scripts runs after the browser meets one more <div>, so its length is 2.
<div>First div</div>

<script>
let divs = [Link]('div');
alert([Link]); // 1
</script>

<div>Second div</div>
<script>
alert([Link]); // 2
</script>
In contrast, querySelectorAll returns a static collection. It’s like a fixed array of elements. If we use it instead,
then both scripts output 1:
<div>First div</div>

<script>
let divs = [Link]('div');
alert([Link]); // 1
</script>

<div>Second div</div>

<script>
alert([Link]); // 1
</script>
Now we can easily see the difference. The static collection did not increase after the appearance of a new div in
the document.

4 Attributes and Properties

Let’s get a more in-depth look at DOM nodes. In this part we’ll see more into what they are and learn their most
used properties.

4.1 Node Properties

4.1.1 Node Class

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, that is
inherited by Node, and other DOM nodes inherit from it. Here’s the picture, explanations to follow:
The classes are:
 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 – 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 – 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 – is finally the basic class for all HTML elements. It is inherited by concrete HTML
elements:
 HTMLInputElement – the class for <input> elements,
 HTMLBodyElement – the class for <body> elements,
 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. For example, let’s consider the DOM object for an <input> element. It belongs to HTMLInputElement
class.
It gets properties and methods as a superposition of (listed in inheritance order):
 HTMLInputElement – this class provides input-specific properties,
 HTMLElement – it provides common HTML element methods (and getters/setters),
 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 “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
the class constructor, and [Link] is its name:
alert( [Link] ); // HTMLBodyElement
Or we can just toString it:
alert( [Link] ); // [object HTMLBodyElement]
We also can use instanceof to check the inheritance:
alert( [Link] instanceof HTMLBodyElement ); // true
alert( [Link] instanceof HTMLElement ); // true
alert( [Link] instanceof Element ); // true
alert( [Link] instanceof Node ); // true
alert( [Link] instanceof EventTarget ); // true
As we can see, DOM nodes are regular JavaScript objects. They use prototype-based classes for inheritance.
That’s also easy to see by outputting an element with [Link](elem) in a browser. There in the console you can
see [Link], [Link] and so on.

4.1.2 The “nodeType” property

The nodeType property provides one more, “old-fashioned” way to get the “type” of a DOM node. It has a
numeric value:
 [Link] == 1 for element nodes,
 [Link] == 3 for text nodes,
 [Link] == 9 for the document object
For instance:
<body>
<script>
let elem = [Link];

// let's examine what it is?


alert([Link]); // 1 => element

// and the first child is...


alert([Link]); // 3 => text

// for the document object, the type is 9


alert( [Link] ); // 9
</script>
</body>
In modern scripts, we can use instanceof and other class-based tests to see the node type, but sometimes
nodeType may be simpler. We can only read nodeType, not change it.

4.1.3 Tag: nodeName and tagName

Given a DOM node, we can read its tag name from nodeName or tagName properties. For instance:
alert( [Link] ); // BODY
alert( [Link] ); // BODY
Is there any difference between tagName and nodeName? Sure, the difference is reflected in their names, but is
indeed a bit subtle.
 The tagName property exists only for Element nodes.
 The nodeName is defined for any Node:
 for elements it means the same as tagName.
 for other node types (text, comment, etc.) it has a string with the node type.
In other words, tagName is only supported by element nodes (as it originates from Element class), while
nodeName can say something about other node types. For instance, let’s compare tagName and nodeName for the
document and a comment node:
<body><!-- comment -->
<script>
// for comment
alert( [Link] ); // undefined (not an element)
alert( [Link] ); // #comment

// for document
alert( [Link] ); // undefined (not an element)
alert( [Link] ); // #document
</script>
</body>
If we only deal with elements, then we can use both tagName and nodeName – there’s no difference.

4.1.4 innerHTML: the contents

The innerHTML property allows to get the HTML inside the element as a string. We can also modify it. So it’s
one of the most powerful ways to change the page. The example shows the contents of [Link] and then
replaces it completely:
<body>
<p>A paragraph</p>
<div>A div</div>

<script>
alert( [Link] ); // read the current contents
[Link] = 'The new BODY!'; // replace it
</script>

</body>
We can try to insert invalid HTML, the browser will fix our errors:
<body>
<script>
[Link] = '<b>test'; // forgot to close the tag
alert( [Link] ); // <b>test</b> (fixed)
</script>
</body>
We can append HTML to an element by using [Link]+="more html". Like this:
[Link] += "<div>Hello<img src='[Link]'/> !</div>";
[Link] += "How goes?";
But we should be very careful about doing it, because what’s going on is not an addition, but a full overwrite.
Technically, these two lines do the same:
[Link] += "...";
// is a shorter way to write:
[Link] = [Link] + "..."
In other words, innerHTML+= does this:
 The old contents is removed.
 The new innerHTML is written instead (a concatenation of the old and the new one).
As the content is “zeroed-out” and rewritten from the scratch, all images and other resources will be reloaded. In
the chatDiv example above the line [Link]+="How goes?" re-creates the HTML content and reloads
[Link] (hope it’s cached). If chatDiv has a lot of other text and images, then the reload becomes clearly visible.
There are other side-effects as well. For instance, if the existing text was selected with the mouse, then most
browsers will remove the selection upon rewriting innerHTML. And if there was an <input> with a text entered by
the visitor, then the text will be removed. And so on. Luckily, there are other ways to add HTML besides
innerHTML, and we’ll study them soon.

4.1.5 outerHTML: full HTML of the element

The outerHTML property contains the full HTML of the element. That’s like innerHTML plus the element itself.
Here’s an example:
<div id="elem">Hello <b>World</b></div>
<script>
alert([Link]); // <div id="elem">Hello <b>World</b></div>
</script>
Beware: unlike innerHTML, writing to outerHTML does not change the element. Instead, it replaces it in the
DOM. Yeah, sounds strange, and strange it is, that’s why we make a separate note about it here. Take a look.
Consider the example:
<div>Hello, world!</div>
<script>
let div = [Link]('div');

// replace [Link] with <p>...</p>


[Link] = '<p>A new element</p>'; // (*)

// Wow! 'div' is still the same!


alert([Link]); // <div>Hello, world!</div> (**)
</script>
Looks really odd, right? In the line (*) we replaced div with <p>A new element</p>. In the outer document (the
DOM) we can see the new content instead of the <div>. But, as we can see in line (**), the value of the old div
variable hasn’t changed!
The outerHTML assignment does not modify the DOM element (the object referenced by, in this case, the
variable ‘div’), but removes it from the DOM and inserts the new HTML in its place. So what happened in
[Link]=... is:
 div was removed from the document.
 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.
It’s so easy to make an error here: modify [Link] 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
[Link], 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.

4.1.6 nodeValue/data: text node content

The innerHTML property is only valid for element nodes. 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. An example of reading the content of a text node
and a comment:
<body>
Hello
<!-- Comment -->
<script>
let text = [Link];
alert([Link]); // Hello

let comment = [Link];


alert([Link]); // Comment
</script>
</body>
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:
<!-- if isAdmin -->
<div>Welcome, Admin!</div>
<!-- /if -->
Then JavaScript can read it from data property and process embedded instructions.

4.1.7 textContent: pure text

The textContent provides access to the text inside the element: only text, minus all <tags>. For instance:
<div id="news">
<h1>Headline!</h1>
<p>Martians attack people!</p>
</div>
<script>
// Headline! Martians attack people!
alert([Link]);
</script>
As we can see, only text is returned, as if all <tags> were cut out, but the text in them remained. In practice,
reading such text is rarely needed. Writing to textContent is much more useful, because it allows to write text the
“safe way”. Let’s say we have an arbitrary string, for instance entered by a user, and want to show it.
 With innerHTML we’ll have it inserted “as HTML”, with all HTML tags.
 With textContent we’ll have it inserted “as text”, all symbols are treated literally.
Compare the two:
<div id="elem1"></div>
<div id="elem2"></div>

<script>
let name = prompt("What's your name?", "<b>Winnie-the-Pooh!</b>");

[Link] = name;
[Link] = name;
</script>
 The first <div> gets the name “as HTML”: all tags become tags, so we see the bold name.
 The second <div> gets the name “as text”, so we literally see <b>Winnie-the-Pooh!</b>.
In most cases, we expect the text from a user, and want to treat it as text. We don’t want unexpected HTML in
our site. An assignment to textContent does exactly that.

4.1.8 The “hidden” property

The “hidden” attribute and the DOM property specifies whether the element is visible or not. We can use it in
HTML or assign it using JavaScript, like this:
<div>Both divs below are hidden</div>
<div hidden>With the attribute "hidden"</div>
<div id="elem">JavaScript assigned the property "hidden"</div>

<script>
[Link] = true;
</script>
Technically, hidden works the same as style="display:none". But it’s shorter to write. Here’s a blinking element:
<div id="elem">A blinking element</div>
<script>
setInterval(() => [Link] = ![Link], 1000);
</script>

4.1.9 More properties

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).
 id – the value of “id” attribute, for all elements (HTMLElement).
 …and much more…
For instance:
<input type="text" id="elem" value="value">
<script>
alert([Link]); // "text"
alert([Link]); // "elem"
alert([Link]); // value
</script>
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. Or if we’d like
to get them fast or are interested in a concrete browser specification – we can always output the element using
[Link](elem) and read the properties. Or explore “DOM properties” in the Elements tab of the browser
developer tools.

4.2 DOM Attributes and Properties

When the browser loads the page, it “reads” (another word: “parses”) the HTML and generates DOM objects
from it. For element nodes, most standard HTML attributes automatically become properties of DOM objects. For
instance, if the tag is <body id="page">, then the DOM object has [Link]="page". But the attribute-property
mapping is not one-to-one! In this chapter we’ll pay attention to separate these two notions, to see how to work
with them, when they are the same, and when they are different.

4.2.1 DOM properties

We’ve already seen built-in DOM properties. There are a lot. But technically no one limits us, and if there aren’t
enough, we can add our own. DOM nodes are regular JavaScript objects. We can alter them. For instance, let’s
create a new property in [Link]:
[Link] = {
name: 'Caesar',
title: 'Imperator'
};

alert([Link]); // Imperator
We can add a method as well:
[Link] = function() {
alert([Link]);
};
[Link](); // BODY (the value of "this" in the method is
[Link])
We can also modify built-in prototypes like [Link] and add new methods to all elements:
[Link] = function() {
alert(`Hello, I'm ${[Link]}`);
};

[Link](); // Hello, I'm HTML


[Link](); // Hello, I'm BODY
So, DOM properties and methods behave just like those of regular JavaScript objects:
 They can have any value.
 They are case-sensitive (write [Link], not [Link]).

4.2.2 HTML attributes

In HTML, tags may have attributes. When the browser parses the HTML to create DOM objects for tags, it
recognizes standard attributes and creates DOM properties from them. So when an element has id or another
standard attribute, the corresponding property gets created. But that doesn’t happen if the attribute is non-standard.
For instance:
<body id="test" something="non-standard">
<script>
alert([Link]); // test
// non-standard attribute does not yield a property
alert([Link]); // undefined
</script>
</body>
Please note that a standard attribute for one element can be unknown for another one. For instance, "type" is
standard for <input> (HTMLInputElement), but not for <body> (HTMLBodyElement). Standard attributes are
described in the specification for the corresponding element class. Here we can see it:
<body id="body" type="...">
<input id="input" type="text">
<script>
alert([Link]); // text
alert([Link]); // undefined: DOM property not created, because it's non-standard
</script>
</body>
So, if an attribute is non-standard, there won’t be a DOM-property for it. Is there a way to access such attributes?
Sure. All attributes are accessible by using the following methods:
 [Link](name) – checks for existence.
 [Link](name) – gets the value.
 [Link](name, value) – sets the value.
 [Link](name) – removes the attribute.
These methods operate exactly with what’s written in HTML. Also one can read all attributes using
[Link]: a collection of objects that belong to a built-in Attr class, with name and value properties. Here’s a
demo of reading a non-standard property:
<body something="non-standard">
<script>
alert([Link]('something')); // non-standard
</script>
</body>
HTML attributes have the following features:
 Their name is case-insensitive (id is same as ID).
 Their values are always strings.
Here’s an extended demo of working with attributes:
<body>
<div id="elem" about="Elephant"></div>

<script>
alert( [Link]('About') ); // (1) 'Elephant', reading

[Link]('Test', 123); // (2), writing

alert( [Link] ); // (3), see if the attribute is in HTML (yes)

for (let attr of [Link]) { // (4) list all


alert( `${[Link]} = ${[Link]}` );
}
</script>
</body>
Please note:
 getAttribute('About') – the first letter is uppercase here, and in HTML it’s all lowercase. But that doesn’t
matter: attribute names are case-insensitive.
 We can assign anything to an attribute, but it becomes a string. So here we have "123" as the value.
 All attributes including ones that we set are visible in outerHTML.
 The attributes collection is iterable and has all the attributes of the element (standard and non-standard) as
objects with name and value properties.

4.2.3 Property-attribute synchronization

When a standard attribute changes, the corresponding property is auto-updated, and (with some exceptions) vice
versa. In the example below id is modified as an attribute, and we can see the property changed too. And then the
same backwards:
<input>
<script>
let input = [Link]('input');
// attribute => property
[Link]('id', 'id');
alert([Link]); // id (updated)

// property => attribute


[Link] = 'newId';
alert([Link]('id')); // newId (updated)
</script>
But there are exclusions, for instance [Link] synchronizes only from attribute → to property, but not back:
<input>
<script>
let input = [Link]('input');

// attribute => property


[Link]('value', 'text');
alert([Link]); // text

// NOT property => attribute


[Link] = 'newValue';
alert([Link]('value')); // text (not updated!)
</script>
In the example above:
 Changing the attribute value updates the property.
 But the property change does not affect the attribute.
That “feature” may actually come in handy, because the user actions may lead to value changes, and then after
them, if we want to recover the “original” value from HTML, it’s in the attribute.

4.2.4 DOM properties are typed

DOM properties are not always strings. For instance, the [Link] property (for checkboxes) is a boolean:
<input id="input" type="checkbox" checked> checkbox
<script>
alert([Link]('checked')); // the attribute value is: empty string
alert([Link]); // the property value is: true
</script>
There are other examples. The style attribute is a string, but the style property is an object:
<div id="div" style="color:red;font-size:120%">Hello</div>
<script>
// string
alert([Link]('style')); // color:red;font-size:120%

// object
alert([Link]); // [object CSSStyleDeclaration]
alert([Link]); // red
</script>
Most properties are strings though. Quite rarely, even if a DOM property type is a string, it may differ from the
attribute. For instance, the href DOM property is always a full URL, even if the attribute contains a relative URL or
just a #hash. Here’s an example:
<a id="a" href="#hello">link</a>
<script>
// attribute
alert([Link]('href')); // #hello

// property
alert([Link] ); // full URL in the form [Link]
</script>
If we need the value of href or any other attribute exactly as written in the HTML, we can use getAttribute.

4.2.5 Non-standard attributes, dataset

When writing HTML, we use a lot of standard attributes. But what about non-standard, custom ones? First, let’s
see whether they are useful or not? What for? Sometimes non-standard attributes are used to pass custom data from
HTML to JavaScript, or to “mark” HTML-elements for JavaScript. Like this:
<!-- mark the div to show "name" here -->
<div show-info="name"></div>
<!-- and age here -->
<div show-info="age"></div>

<script>
// the code finds an element with the mark and shows what's requested
let user = {
name: "Pete",
age: 25
};

for(let div of [Link]('[show-info]')) {


// insert the corresponding info into the field
let field = [Link]('show-info');
[Link] = user[field]; // first Pete into "name", then 25 into "age"
}
</script>
Also they can be used to style an element. For instance, here for the order state the attribute order-state is used:
<style>
/* styles rely on the custom attribute "order-state" */
.order[order-state="new"] {
color: green;
}

.order[order-state="pending"] {
color: blue;
}

.order[order-state="canceled"] {
color: red;
}
</style>

<div class="order" order-state="new">


A new order.
</div>

<div class="order" order-state="pending">


A pending order.
</div>

<div class="order" order-state="canceled">


A canceled order.
</div>
Why would using an attribute be preferable to having classes like .order-state-new, .order-state- pending, .order -
state-canceled? Because an attribute is more convenient to manage. The state can be changed as easy as:
// a bit simpler than removing old/adding a new class
[Link]('order-state', 'canceled');
But there may be a possible problem with custom attributes. What if we use a non-standard attribute for our
purposes and later the standard introduces it and makes it do something? The HTML language is alive, it grows,
and more attributes appear to suit the needs of developers. There may be unexpected effects in such case. To avoid
conflicts, there exist data-* attributes. All attributes starting with “data-” are reserved for programmers’ use. They
are available in the dataset property. For instance, if an elem has an attribute named "data-about", it’s available as
[Link]. Like this:
<body data-about="Elephants">
<script>
alert([Link]); // Elephants
</script>
Multiword attributes like data-order-state become camel-cased: [Link]. Here’s a rewritten “order
state” example:
<style>
.order[data-order-state="new"] {
color: green;
}
.order[data-order-state="pending"] {
color: blue;
}

.order[data-order-state="canceled"] {
color: red;
}
</style>

<div id="order" class="order" data-order-state="new">


A new order.
</div>

<script>
// read
alert([Link]); // new

// modify
[Link] = "pending"; // (*)
</script>
Using data-* attributes is a valid, safe way to pass custom data. Please note that we can not only read, but also
modify data-attributes. Then CSS updates the view accordingly: in the example above the last line (*) changes the
color to blue.

5 Modify the Document

DOM modification is the key to creating “live” pages. Here we’ll see how to create new elements “on the fly”
and modify the existing page content.
Let’s demonstrate using an example. We’ll add a message on the page that looks nicer than alert. Here’s how it
will look:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<div class="alert">
<strong>Hi there!</strong> You've read an important message.
</div>
That was the HTML example. Now let’s create the same div with JavaScript (assuming that the styles are in the
HTML/CSS already).

5.1 Modify Elements

5.1.1 Creating an element

To create DOM nodes, there are two methods:


[Link](tag)
Creates a new element node with the given tag:
let div = [Link]('div');
[Link](text)
Creates a new text node with the given text:
let textNode = [Link]('Here I am');
Most of the time we need to create element nodes, such as the div for the message.

5.1.2 Creating the message

Creating the message div takes 3 steps:


// 1. Create <div> element
let div = [Link]('div');

// 2. Set its class to "alert"


[Link] = "alert";

// 3. Fill it with the content


[Link] = "<strong>Hi there!</strong> You've read an important message.";
We’ve created the element. But as of now it’s only in a variable named div, not in the page yet. So we can’t see
it.

5.1.3 Insertion methods

To make the div show up, we need to insert it somewhere into document. For instance, into <body> element,
referenced by [Link]. There’s a special method append for that: [Link](div). Here’s the
full code:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>

<script>
let div = [Link]('div');
[Link] = "alert";
[Link] = "<strong>Hi there!</strong> You've read an important message.";

[Link](div);
</script>
Here we called append on [Link], but we can call append method on any other element, to put another
element into it. For instance, we can append something to <div> by calling [Link](anotherElement). Here are
more insertion methods, they specify different places where to insert:
 [Link](...nodes or strings) – append nodes or strings at the end of node,
 [Link](...nodes or strings) – insert nodes or strings at the beginning of node,
 [Link](...nodes or strings) –- insert nodes or strings before node,
 [Link](...nodes or strings) –- insert nodes or strings after node,
 [Link](...nodes or strings) –- replaces node with the given nodes or strings.
Arguments of these methods are an arbitrary list of DOM nodes to insert, or text strings (that become text
nodes automatically). Let’s see them in action. Here’s an example of using these methods to add items to a list and
the text before/after it:
<ol id="ol">
<li>0</li>
<li>1</li>
<li>2</li>
</ol>

<script>
[Link]('before'); // insert string "before" before <ol>
[Link]('after'); // insert string "after" after <ol>

let liFirst = [Link]('li');


[Link] = 'prepend';
[Link](liFirst); // insert liFirst at the beginning of <ol>

let liLast = [Link]('li');


[Link] = 'append';
[Link](liLast); // insert liLast at the end of <ol>
</script>
Here’s a visual picture of what the methods do:
So the final list will be:
before
<ol id="ol">
<li>prepend</li>
<li>0</li>
<li>1</li>
<li>2</li>
<li>append</li>
</ol>
after
As said, these methods can insert multiple nodes and text pieces in a single call. For instance, here a string and
an element are inserted:
<div id="div"></div>
<script>
[Link]('<p>Hello</p>', [Link]('hr'));
</script>
Please note: the text is inserted “as text”, not “as HTML”, with proper escaping of characters such as <, >. So the
final HTML is:
&lt;p&gt;Hello&lt;/p&gt;
<hr>
<div id="div"></div>
In other words, strings are inserted in a safe way, like [Link] does it. So, these methods can only be
used to insert DOM nodes or text pieces. But what if we’d like to insert an HTML string “as html”, with all tags
and stuff working, in the same manner as [Link] does it?

5.1.4 insertAdjacentHTML/Text/Element

For that we can use another, pretty versatile method: [Link](where, html). The first
parameter is a code word, specifying where to insert relative to elem. Must be one of the following:
 "beforebegin" – insert html immediately before elem,
 "afterbegin" – insert html into elem, at the beginning,
 "beforeend" – insert html into elem, at the end,
 "afterend" – insert html immediately after elem.
The second parameter is an HTML string, that is inserted “as HTML”. For instance:
<div id="div"></div>
<script>
[Link]('beforebegin', '<p>Hello</p>');
[Link]('afterend', '<p>Bye</p>');
</script>
Would lead to:
<p>Hello</p>
<div id="div"></div>
<p>Bye</p>
That’s how we can append arbitrary HTML to the page. Here’s the picture of insertion variants:

We can easily notice similarities between this and the previous picture. The insertion points are actually the
same, but this method inserts HTML. The method has two brothers:
 [Link](where, text) – the same syntax, but a string of text is inserted “as text” instead of
HTML,
 [Link](where, elem) – the same syntax, but inserts an element.
They exist mainly to make the syntax “uniform”. In practice, only insertAdjacentHTML is used most of the
time. Because for elements and text, we have methods append/prepend/before/after – they are shorter to write and
can insert nodes/text pieces. So here’s an alternative variant of showing a message:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>

<script>
[Link]("afterbegin", `<div class="alert">
<strong>Hi there!</strong> You've read an important message.
</div>`);
</script>
5.1.5 Node removal

To remove a node, there’s a method [Link](). Let’s make our message disappear after a second:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>

<script>
let div = [Link]('div');
[Link] = "alert";
[Link] = "<strong>Hi there!</strong> You've read an important message.";

[Link](div);
setTimeout(() => [Link](), 1000);
</script>
Please note: if we want to move an element to another place – there’s no need to remove it from the old one. All
insertion methods automatically remove the node from the old place. For instance, let’s swap elements:
<div id="first">First</div>
<div id="second">Second</div>
<script>
// no need to call remove
[Link](first); // take #second and after it insert #first
</script>

5.1.6 Cloning nodes: cloneNode

How to insert one more similar message? We could make a function and put the code there. But the alternative
way would be to clone the existing div and modify the text inside it (if needed). Sometimes when we have a big
element, that may be faster and simpler. The call [Link](true) creates a “deep” clone of the element – with
all attributes and subelements. If we call [Link](false), then the clone is made without child elements. An
example of copying the message:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>

<div class="alert" id="div">


<strong>Hi there!</strong> You've read an important message.
</div>

<script>
let div2 = [Link](true); // clone the message
[Link]('strong').innerHTML = 'Bye there!'; // change the clone

[Link](div2); // show the clone after the existing div


</script>

5.1.7 [Link]

There’s one more, very ancient method of adding something to a web-page: [Link]. The syntax:
<p>Somewhere in the page...</p>
<script>
[Link]('<b>Hello from JS</b>');
</script>
<p>The end</p>
The call to [Link](html) writes the html into page “right here and now”. The html string can be
dynamically generated, so it’s kind of flexible. We can use JavaScript to create a full-fledged webpage and write it.
The method comes from times when there was no DOM, no standards… Really old times. It still lives, because
there are scripts using it. In modern scripts we can rarely see it, because of the following important limitation:
 The call to [Link] only works while the page is loading.
 If we call it afterwards, the existing document content is erased.
For instance:
<p>After one second the contents of this page will be replaced...</p>
<script>
// [Link] after 1 second
// that's after the page loaded, so it erases the existing content
setTimeout(() => [Link]('<b>...By this.</b>'), 1000);
</script>
So it’s kind of unusable at “after loaded” stage, unlike other DOM methods we covered above. That’s the
downside. There’s an upside also. Technically, when [Link] is called while the browser is reading
(“parsing”) incoming HTML, and it writes something, the browser consumes it just as if it were initially there, in
the HTML text. So it works blazingly fast, because there’s no DOM modification involved. It writes directly into
the page text, while the DOM is not yet built. So if we need to add a lot of text into HTML dynamically, and we’re
at page loading phase, and the speed matters, it may help. But in practice these requirements rarely come together.
And usually we can see this method in scripts just because they are old.
5.2 Modify CSS Styles

Before we get into JavaScript’s ways of dealing with styles and classes – here’s an important rule. Hopefully it’s
obvious enough, but we still have to mention it. There are generally two ways to style an element:
 Create a class in CSS and add it: <div class="...">
 Write properties directly into style: <div style="...">.
JavaScript can modify both classes and style properties. We should always prefer CSS classes to style. The latter
should only be used if classes “can’t handle it”. For example, style is acceptable if we calculate coordinates of an
element dynamically and want to set them from JavaScript, like this:
let top = /* complex calculations */;
let left = /* complex calculations */;

[Link] = left; // e.g '123px', calculated at run-time


[Link] = top; // e.g '456px'
For other cases, like making the text red, adding a background icon – describe that in CSS and then add the class
(JavaScript can do that). That’s more flexible and easier to support.

5.2.1 className and classList

Changing a class is one of the most often used actions in scripts. In the ancient time, there was a limitation in
JavaScript: a reserved word like "class" could not be an object property. That limitation does not exist now, but at
that time it was impossible to have a "class" property, like [Link]. So for classes the similar-looking property
"className" was introduced: the [Link] corresponds to the "class" attribute. For instance:
<body class="main page">
<script>
alert([Link]); // main page
</script>
</body>
If we assign something to [Link], it replaces the whole string of classes. Sometimes that’s what we
need, but often we want to add/remove a single class. There’s another property for that: [Link]. The
[Link] is a special object with methods to add/remove/toggle a single class. For instance:
<body class="main page">
<script>
// add a class
[Link]('article');

alert([Link]); // main page article


</script>
</body>
So we can operate both on the full class string using className or on individual classes using classList. What we
choose depends on our needs.
Methods of classList:
 [Link]/remove("class") – adds/removes the class.
 [Link]("class") – adds the class if it doesn’t exist, otherwise removes it.
 [Link]("class") – checks for the given class, returns true/false.
Besides, classList is iterable, so we can list all classes with for..of, like this:
<body class="main page">
<script>
for (let name of [Link]) {
alert(name); // main, and then page
}
</script>
</body>

5.2.2 Element style

The property [Link] is an object that corresponds to what’s written in the "style" attribute. Setting
[Link]="100px" works the same as if we had in the attribute style a string width:100px. For multi-word
property the camelCase is used:
 background-color => [Link]
 z-index => [Link]
 border-left-width => [Link]
For instance:
[Link] = prompt('background color?', 'green');
Browser-prefixed properties like -moz-border-radius, -webkit-border-radius also follow the same rule: a dash
means upper case. For instance:
[Link] = '5px';
[Link] = '5px';

5.2.3 Resetting the style property

Sometimes we want to assign a style property, and later remove it. For instance, to hide an element, we can set
[Link] = "none". Then later we may want to remove the [Link] as if it were not set. Instead of
delete [Link] we should assign an empty string to it: [Link] = "".
// if we run this code, the <body> will blink
[Link] = "none"; // hide

setTimeout(() => [Link] = "", 1000); // back to normal


If we set [Link] to an empty string, then the browser applies CSS classes and its built-in styles normally, as
if there were no such [Link] property at all.
Normally, we use style.* to assign individual style properties. We can’t set the full style like [Link]="color:
red; width: 100px", because [Link] is an object, and it’s read-only. To set the full style as a string, there’s a special
property [Link]:
<div id="div">Button</div>

<script>
// we can set special style flags like "important" here
[Link]=`color: red !important;
background-color: yellow;
width: 100px;
text-align: center;
`;

alert([Link]);
</script>
This property is rarely used, because such assignment removes all existing styles: it does not add, but replaces
them. May occasionally delete something needed. But we can safely use it for new elements, when we know we
won’t delete an existing style. The same can be accomplished by setting an attribute: [Link]('style', 'color:
red...').
Don’t forget to add CSS units to values. For instance, we should not set [Link] to 10, but rather to 10px.
Otherwise it wouldn’t work:
<body>
<script>
// doesn't work!
[Link] = 20;
alert([Link]); // '' (empty string, the assignment is ignored)

// now add the CSS unit (px) - and it works


[Link] = '20px';
alert([Link]); // 20px

alert([Link]); // 20px
alert([Link]); // 20px
</script>
</body>
Please note: the browser “unpacks” the property [Link] in the last lines and infers [Link] and
[Link] from it.

5.2.4 Computed styles: getComputedStyle

So, modifying a style is easy. But how to read it? For instance, we want to know the size, margins, the color of an
element. How to do it? The style property operates only on the value of the "style" attribute, without any CSS
cascade. So we can’t read anything that comes from CSS classes using [Link]. For instance, here style doesn’t
see the margin:
<head>
<style> body { color: red; margin: 5px } </style>
</head>
<body>
The red text
<script>
alert([Link]); // empty
alert([Link]); // empty
</script>
</body>
But what if we need, say, to increase the margin by 20px? We would want the current value of it. There’s another
method for that: getComputedStyle. The syntax is:
getComputedStyle(element, [pseudo])
 element: Element to read the value for.
 pseudo: A pseudo-element if required, for instance ::before. An empty string or no argument means the
element itself.
The result is an object with styles, like [Link], but now with respect to all CSS classes. For instance:
<head>
<style> body { color: red; margin: 5px } </style>
</head>
<body>

<script>
let computedStyle = getComputedStyle([Link]);

// now we can read the margin and the color from it

alert( [Link] ); // 5px


alert( [Link] ); // rgb(255, 0, 0)
</script>

</body>
There are two concepts in CSS:
 A computed style value is the value after all CSS rules and CSS inheritance is applied, as the result of the CSS
cascade. It can look like height:1em or font-size:125%.
 A resolved style value is the one finally applied to the element. Values like 1em or 125% are relative. The
browser takes the computed value and makes all units fixed and absolute, for instance: height:20px or font-
size:16px. For geometry properties resolved values may have a floating point, like width:50.5px.
A long time ago getComputedStyle was created to get computed values, but it turned out that resolved values
are much more convenient, and the standard changed. So nowadays getComputedStyle actually returns the resolved
value of the property, usually in px for geometry.
6 Summary

The Document Object Model (DOM) is a language-independent API for accessing and manipulating HTML and
XML documents. DOM Level 1 deals with representing HTML and XML documents as a hierarchy of nodes that
can be manipulated to change the appearance and structure of the underlying documents using JavaScript.
The DOM is made up of a series of node types, as described here:
The base node type is Node , which is an abstract representation of an individual part of a document; all other
types inherit from Node .
The Document type represents an entire document and is the root node of a hierarchy. In JavaScript, the
document object is an instance of Document , which allows for querying and retrieval of nodes in a number of
different ways.
An Element node represents all HTML or XML elements in a document and can be used to manipulate their
contents and attributes.
Other node types exist for text contents, comments, document types, the CDATA section, and document
fragments.
They all have the following characteristics:
DOM access works as expected in most cases, although there are often complications when working with
<script> and <style> elements. Since these elements contain scripting and stylistic information, respectively, they
are often treated differently in browsers than other elements. Perhaps the most important thing to understand about
the DOM is how it affects overall performance. DOM manipulations are some of the most expensive operations
that can be done in JavaScript, with NodeList objects being particularly troublesome. NodeList objects are “live,”
meaning that a query is run every time the object is accessed. Because of these issues, it is best to minimize the
number of DOM manipulations.
Each of the wrapper types maps to the primitive type of the same name. When a primitive value is accessed in
read mode, a primitive wrapper object is instantiated so that it can be used to manipulate the data. As soon as a
statement involving a primitive value is executed, the wrapper object is destroyed. There are also two built-in
objects that exist at the beginning of code execution: Global and Math. The Global object isn’t accessible in most
ECMAScript implementations; however, web browsers implement it as the window object. The Global object
contains all global variables and functions as properties. The Math object contains properties and methods to aid in
complex mathematical calculations.
Assignment
1) Look at this page:
<html>
<body>
<div>Users:</div>
<ul>
<li>John</li>
<li>Pete</li>
</ul>
</body>
</html>
For each of the following, give at least one way of how to access them:
a) The <div> DOM node?
b) The <ul> DOM node?
c) The second <li> (with Pete)?
2) If elem – is an arbitrary DOM element node…
Is it true that [Link] is always null?
Is it true that [Link][0].previousSibling is always null ?
3) Write the code to paint all diagonal table cells in red. You’ll need to get all diagonal <td> from the <table> and
paint them using the code:
// td should be the reference to the table cell
[Link] = 'red';
The result should be:

4) Here’s the document with the table and form.


The table with id="age-table".
All label elements inside that table (there should be 3 of them).
The first td in that table (with the word “Age”).
The form with name="search".
The first input in that form.
The last input in that form.
The code is:
<!DOCTYPE HTML>
<html>
<body>
<form name="search">
<label>Search the site:
<input type="text" name="search">
</label>
<input type="submit" value="Search!">
</form>

<hr>

<form name="search-person">
Search the visitors:
<table id="age-table">
<tr>
<td>Age:</td>
<td id="age-list">
<label>
<input type="radio" name="age" value="young">less than 18</label>
<label>
<input type="radio" name="age" value="mature">18-50</label>
<label>
<input type="radio" name="age" value="senior">more than 50</label>
</td>
</tr>

<tr>
<td>Additionally:</td>
<td>
<input type="text" name="info[0]">
<input type="text" name="info[1]">
<input type="text" name="info[2]">
</td>
</tr>

</table>

<input type="submit" value="Search!">


</form>
</body>
</html>
5) Write the code to select the element with data-widget-name attribute from the document and to read its value.
<!DOCTYPE html>
<html>
<body>
<div data-widget-name="menu">Choose the genre</div>

<script>
/* your code */
</script>
</body>
</html>
6) Make all external links orange by altering their style property. A link is external if:
Its href has :// in it
But doesn’t start with [Link]
Example:
<a name="list">the list</a>
<ul>
<li><a href="[Link]
<li><a href="/tutorial">/[Link]</a></li>
<li><a href="local/path">local/path</a></li>
<li><a href="[Link]
<li><a href="[Link]
<li><a href="[Link]
</ul>

<script>
// setting style for a single link
let link = [Link]('a');
[Link] = 'orange';
</script>
The result should be:

7) We have an empty DOM element elem and a string text. Which of these 3 commands will do exactly the
same?
[Link]([Link](text))
[Link] = text
[Link] = text
8) Create a function clear(elem) that removes everything from the element.
<ol id="elem">
<li>Hello</li>
<li>World</li>
</ol>

<script>
function clear(elem) { /* your code */ }

clear(elem); // clears the list


</script>
9) In the example below, the call [Link]() removes the table from the document. But if you run it, you can
see that the text "aaa" is still visible. Why does that happen?
<table id="table">
aaa
<tr>
<td>Test</td>
</tr>
</table>

<script>
alert(table); // the table, as it should be

[Link]();
// why there's still aaa in the document?
</script>
10) Write an interface to create a list from user input. For every list item:
 Ask a user about its content using prompt.
 Create the <li> with it and add it to <ul>.
 Continue until the user cancels the input (by pressing Esc or via an empty entry).
All elements should be created dynamically. If a user types HTML-tags, they should be treated like a text.
11) Write a function createTree that creates a nested ul/li list from the nested object. For instance:
let data = {
"Fish": {
"trout": {},
"salmon": {}
},

"Tree": {
"Huge": {
"sequoia": {},
"oak": {}
},
"Flowering": {
"apple tree": {},
"magnolia": {}
}
}
};
The syntax:

let container = [Link]('container');


createTree(container, data); // creates the tree in the container
The result (tree) should look like this:

Choose one of two ways of solving this task:


 Create the HTML for the tree and then assign to [Link].
 Create tree nodes and append with DOM methods.
Would be great if you could do both. P.S. The tree should not have “extra” elements like empty <ul></ul> for the
leaves.
12) There’s a tree organized as nested ul/li. Write the code that adds to each <li> the number of its descendants.
Skip leaves (nodes without children). The result:

13) Write a function createCalendar(elem, year, month). The call should create a calendar for the given year/month
and put it inside elem. The calendar should be a table, where a week is <tr>, and a day is <td>. The table top
should be <th> with weekday names: the first day should be Monday, and so on till Sunday. For instance,
createCalendar(cal, 2012, 9) should generate in element cal the following calendar:
P.S. For this task it’s enough to generate the calendar, should not yet be clickable.
14) Create a colored clock like here:

Use HTML/CSS for the styling, JavaScript only updates time in elements.
15) Write the code to insert <li>2</li><li>3</li> between two <li> here:
<ul id="ul">
<li id="one">1</li>
<li id="two">4</li>
</ul>
16) There’s a table:
<table>
<thead>
<tr>
<th>Name</th><th>Surname</th><th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td><td>Smith</td><td>10</td>
</tr>
<tr>
<td>Pete</td><td>Brown</td><td>15</td>
</tr>
<tr>
<td>Ann</td><td>Lee</td><td>5</td>
</tr>
<tr>
<td>...</td><td>...</td><td>...</td>
</tr>
</tbody>
</table>
There may be more rows in it. Write the code to sort it by the "name" column.
17) Write a function showNotification(options) that creates a notification: <div class="notification"> with the
given content. The notification should automatically disappear after 1.5 seconds.
Use CSS positioning to show the element at given top/right coordinates. The source document has the necessary
styles.
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" href="[Link]">
</head>
<body>
<h2>Notification is on the right</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex
eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus
cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum
magnam consequatur sit laboriosam velit excepturi laborum sequi eos placeat et quia
deleniti? Corrupti velit impedit autem et obcaecati fuga debitis nemo ratione iste veniam
amet dicta hic ipsam unde cupiditate incidunt aut iure ipsum officiis soluta
temporibus. Tempore dicta ullam delectus numquam consectetur quisquam explicabo
culpa excepturi placeat quo sequi molestias reprehenderit hic at nemo cumque voluptates
quidem repellendus maiores unde earum molestiae ad.
</p>
<script>
function showNotification({top = 0, right = 0, className, html}) {
// To Be Fixed
}

// test it
let i = 1;
setInterval(() => {
showNotification({
top: 10,
right: 10,
html: 'Hello ' + i++,
className: "welcome"
});
}, 2000);
</script>

</body>
</html>

You might also like