The Document Object Model (DOM) and the DOM API have been mentioned several times already in this book, but now it's time to step back and look at them in depth.
As you will see over the next few lessons, you can largely avoid an in-depth understanding of the DOM API if you use jQuery. jQuery is essentially a wrapper around the DOM; it provides all the same basic functionality but with a more intuitive API.
It is, however, wise to have at least a basic understanding of how the underlying DOM technology works before starting with jQuery because this places it in a wider context, and helps you understand what jQuery is trying to achieve.
The Document Object Model is the in-memory browser representation of a web page. When the browser loads a web page, it parses all the HTML tags and their content, and generates a model for display in the browser.
As you have already seen, the DOM model may differ from the literal HTML in several ways. For example:
body
tag.You have also seen how the DOM is represented as a tree-like structure via the Elements tab in the Chrome developer tools.
The DOM is actually more complex than the Elements tab implies. In order to understand the DOM, you need to think in terms of nodes rather than elements: The DOM is a hierarchy of nodes.
In this section you will gain an understanding of the DOM constructed for the following web page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<h1>This is the header</h1>
</body>
</html>
The DOM for this HTML will be modeled as you see in Figure 16.1. Each square box in this diagram is called a node in the DOM tree. As you can see, there are many nodes that are not simple elements in the document.
Figure 16.1 also lists the interface or object type of each node. The DOM represents each node as an object (a JavaScript object in this case), and this object exposes an interface consisting of a set of properties and methods.
You can see that the top-level node is called the document
, and the html
element is its child. Along with the element
nodes, you can also see examples of text
and attr
nodes in the tree because the DOM models these as independent objects.
The following are the most common types of object:
document
: A document object represents the entire document. This can be accessed within the browser using the global variable document
. When you start using the DOM API shortly, you will always do so from the context of the document
.element
: This object is used to represent the elements, or tags, in the document. Every tag—such as td
, head
, or h1
—is represented by an instance of this object, but their content and attributes are modeled as independent objects.attr
: The attr
object type represents attributes. Each element can have zero to many attr
objects as its children.text
: When the body of an element contains text, this is represented by a separate object called the text
object. In some cases an element can have multiple text nodes. For instance, with the following HTML fragment <p>Left side<em>inner</em>right side</p>
, the p
tag has two text nodes, one for the text on either side of the em
tag, while the em
tag also has a text node.You may be wondering why you need all these different object types. The primary reason is that you want to do different things to different nodes in the document. For instance, the API exposed by the document
may contain functionality to determine the doctype
, whereas the API exposed by the attr
object may contain functionality to change the attribute value. Alternatively, the element
object allows you to navigate, add, remove, and change its child nodes, whereas this is not possible with text
nodes.
The API used for interacting with, and manipulating all these types of node is called the DOM API. Browsers implement the DOM API in JavaScript, but the DOM API can be written in any language and can exist outside a web browser.
The purpose of the DOM API is mainly to allow the DOM to be manipulated after the web page has loaded. This allows you to implement dynamic functionality within web pages without resorting to page refreshes. There are four essential aspects to the DOM API that allow this manipulation:
Each of these will be briefly discussed in the text that follows. Before starting, it is worth mentioning that the DOM API is very large, and I will only scratch the surface of what is possible. The idea is to be introduced to broad themes rather than understand every aspect of the API.
The examples that follow will use the following web page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<h1 class="mainHeader">This is the title</h1>
<ol id="daysOfWeek">
<li>Monday
<li>Tuesday
<li>Wednesday
<li>Thursday
<li>Friday
</ol>
</body>
</html>
You can either download this from the book's website (it is called domexample.html
), or you can write it yourself.
This web page contains a heading and a list of days in an ordered list. You will interact with this web page from the Chrome console, but the code could also be added to a script block in the page itself.
You select elements from the DOM using the same basic criteria as CSS: Specifically, you select elements by their type, their classes, or their IDs.
To select an element by ID, you can use the following method on the document
object:
> document.getElementById('daysOfWeek'),
This returns a single object, which is why it is very important you never duplicate IDs on a web page.
Whenever you select an object from the DOM, you can determine its type by using the nodeType
property. For example:
> document.getElementById('daysOfWeek').nodeType;
1
The DOM API uses numbers to represent the various types of node: 1 represents an element node, 2 represents an attribute node, 3 represents a text node, and so on.
Alternatively, you can determine the name of the element:
> document.getElementById('daysOfWeek').nodeName;
"OL"
It is also possible to select nodes by class name:
> document.getElementsByClassName('mainHeader'),
or tag name:
> document.getElementsByTagName('li')
Both of these methods return an array of objects rather than a single object. Therefore, if you know only a single object will be returned, you still need to access it from the first index in the array before using it:
> document.getElementsByTagName('ol')[0].nodeName
"OL"
Once nodes have been selected, it is common to navigate from these nodes to other nodes. The examples that follow demonstrate how it is possible to navigate from nodes to their children, and from child nodes to parents:
// obtain a reference to the daysOfWeek node
o = document.getElementById('daysOfWeek'),
// find all the children for this node (i.e., the li elements)
o.childNodes
// find the parent of this node, i.e., the body element
o.parentNode
// find the first child of this node
o.firstElementChild
The DOM API also supports a number of shortcuts for traversing from one node to another. For instance, you can traverse from the document
node to the body
node as follows:
> document.body
Selection and traversal operations are used to select a set of nodes; these can then be manipulated to dynamically change the appearance of the web page after it has loaded in the browser.
For example, you may want to add a new li
element with the value Saturday
. This is a two-part process: First, you need to construct the nodes that represent the new li
element, and then you need to insert it into the DOM at the appropriate location.
To start, you will create the li
node, and assign this to the variable newLi
. The node created will not be part of the DOM at this stage; it will be a standalone DOM object, commonly referred to as a document fragment.
> newLi = document.createElement('li'),
Next, you will create a new text
node, also using a method on the document
itself. Like the li
node, this will not be part of the DOM:
> saturday = document.createTextNode("Saturday");
Next, you will set the text
node to be the first child of the new li
node:
> newLi.appendChild(saturday);
Finally, you can add the li
node into the DOM at the appropriate location. Many methods are available for controlling where nodes are inserted in relation to other nodes, but you will use the appendChild
method; this simply adds the node as the last child of an existing node:
> document.getElementById('daysOfWeek').appendChild(newLi);
As soon as you invoke this final line, a new li
element will appear in the web page. In addition, if you look at the Elements tab in the developer tool, you can see that the DOM has been updated (see Figure 16.2).
As you can see, working with the DOM API is somewhat convoluted: This code will be significantly simplified once you introduce jQuery.
The final aspect of the DOM API you need to understand is events. In fact, you have already seen these when you wrote the drag-and-drop–based web page in Lesson 9.
Each type of DOM object supports a wide variety of events. These are primarily categorized as:
keyup
and keydown
onclick
and onmousedown
onload
and onresize
onchange
and onselect
In this section, you will take advantage of two mouse events. You will add functionality so that if the user hovers over the header (a mouseenter
event), the list will be displayed (its display
property will be set to block
). When the mouse moves away from the header (a mouseleave
event), the list will be hidden
again.
You will change the web page so that the list of days is initially hidden by adding the following to the head
element:
<style>
ol {
display:none;
}
</style>
Next you will add the appropriate event listeners in the script
block before the closing body
tag.
<script>
document.getElementsByClassName('mainHeader')[0]
.addEventListener("mouseenter", function(event) {
document.getElementById('daysOfWeek').style.display = 'block';
});
document.getElementsByClassName('mainHeader')[0]
.addEventListener("mouseleave", function(event) {
document.getElementById('daysOfWeek').style.display = 'none';
});
</script>
In this example, you add two event listeners to the h1
element. One is fired when the mouse enters the box of the header element; the other fires when the mouse leaves the box.
Within each event listener you simply locate the ol
node, and either hide it or show it as appropriate. This is achieved by updating its display
style to either block
or none
.
In this Try It, you will take the CRM web application from Lesson 14 and add two dynamic features to it:
In order to complete this lesson, you will need the Chrome web browser and the CRM project files from Lesson 14.
This step-by-step will be broken into two distinct sections. Work through each in turn, ensuring that the functionality works as expected before moving on.
id contactDetails
in the HTML. This can be accomplished via an inline style on the element itself, which will set its display
property to none
.contactList
section (after the closing table
tag) that the user can click to add a new contact. The href
for this should simply be #
because you will not use browser-based navigation (you will respond to the user click in JavaScript). You need to also ensure the hyperlink has an id
so you can select it from the DOM. You can choose how this looks, but I added the following:
<div class="controls">
<a href="#" id="addContact">Add a new contact</a>
</div>
I also added the following to contacts.css
to improve its appearance:
.controls {
padding:15px;
}
init
method of contacts.js
, select the hyperlink by id
using document.getElementById
, and add an event listener to the object returned using addEventListsner
. The first parameter to addEventListener
is the type of event you are listening for, which in this case is click
. The second parameter is a function that accepts a single parameter called event
. Create this as an anonymous function.preventDefault()
on the event
itself.contactDetails
by id
, and set its style.display
property to block
.You will now change the time field so that when the user hovers above it, the notes are displayed, as shown in Figure 16.3.
time
elements, and modify them so that they contain an additional element with notes information. For example:
<td>
<time datetime="2014-09-12">2014-09-12</time>
<div class="overlay">These are my notes for William</div>
</td>
Notice that I have also added a class called overlay
to the div
.
height
of the div
to 100px.width
of the div
to 300px.border
to the div
with a color of #333333
.#eeeeee
.display
to none
.position
element to remove the element from the flow of the page, and ensure that, when it is displayed, it does not impact the position of any other elements in the DOM. Think about which position
value most closely meets these needs.z-index
so that when the div
is displayed, it appears over the top of the table.My CSS looked like this:
.overlay {
position: fixed;
height: 100px;
width: 300px;
border: 1px solid #333333;
background: #eeeeee;
display: none;
z-index:1;
padding:10px;
}
time
elements. Select all the time
elements using document.getElementsByTagName
. Once these have been found and stored in a local variable, iterate through them in a for
loop.for
loop, you need to add two different event listeners, one for mouseenter
and one for mouseleave
. Create skeleton implementations for each of these and make sure the anonymous function passed as their second parameter accepts the parameter event
.div
that is closest to the time
element that the mouse is hovering over. You can first find the time
element they are hovering over using event.target
. You can then find the next sibling of this element using nextElementSibling
and set the display
property to either block
or none
, depending on whether you are hiding or showing the div
.
The next sibling element allows you to find the next element in the DOM with the same parent as the specified element. In the case of the time
element, that will be the div
element containing the notes.
time
elements, the notes should display and then immediately disappear if you hover away.18.224.30.19