Understanding the Document Object Model
Understanding the Document Object Model
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:
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";
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
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.
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.
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.
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.
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.
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:
<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”.
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.
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>
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
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>
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 ✔ ✔
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');
<script>
// elem is a reference to DOM-element with id="elem"
[Link] = 'red';
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');
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
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];
<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.
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.
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.
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];
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.
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.
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');
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
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.
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>
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.
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.
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]}`);
};
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
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)
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.
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
};
.order[order-state="pending"] {
color: blue;
}
.order[order-state="canceled"] {
color: red;
}
</style>
.order[data-order-state="canceled"] {
color: red;
}
</style>
<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.
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).
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>
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>
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>
<script>
let div2 = [Link](true); // clone the message
[Link]('strong').innerHTML = 'Bye there!'; // change the clone
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 */;
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');
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';
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
<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)
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.
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]);
</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:
<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>
<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 */ }
<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:
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>