Document Object Model

  • Games
  • Scrolling Effects
  • Dropdown menus
  • Form Validations
  • Interactivity
  • Animations
  • Every awesome site ever

Why Should You Care?

[1, 2, 5, 6, -1, 8, 11]

In File

In Memory

     1  
   /  \  
  2    5  
 / \  / \  
6 -1 8  11  

Tree Representation

Getting Started

  • The Document Object Model (a.k.a. the DOM) Is a Hierarchy/Tree of JavaScript Node Objects
  • The Document Object Model (DOM) is a data structure that lets you manipulate the underlying document elements and properties using JavaScript.

Definitions:

The Document Object Model is a platform- and language-neutral interface that will allow programs and scripts to dynamically access and update the content, structure and style of documents. The document can be further processed and the results of that processing can be incorporated back into the presented page. - W3C

What is DOM?

  • When you write an HTML document, you encapsulate HTML content inside other HTML content.
  • By doing this, you set up a hierarchy of nodes that can be expressed as a "tree".
<html>
  <body>
    <p>
      Hello World
    </p>
    <div> <img src="example.png"/></div>
  </body>
</html>

The output tree (the "parse tree") is a tree of DOM element and attribute nodes. DOM is short for Document Object Model.

It is the object presentation of the HTML document and the interface of HTML elements to the outside world like JavaScript.

Node Object Types

  • DOCUMENT_NODE
    • e.g., window.document
  • ELEMENT_NODE
    • e.g., <body>, <a>, <p>, <script>, <style>, <html>
  • ATTRIBUTE_NODE
    • e.g., class="funEdges"
  • TEXT_NODE
    • e.g., text characters in an HTML document including carriage return and whitespace
  • DOCUMENT_FRAGMENT_NODE
    • e.g., document.createDocumentFragment()
  • DOCUMENT_TYPE_NODE
    • e.g., <!DOCTYPE html>

There are more ...

ATTRIBUTE_NODE is not actually part of a tree, but rather is listed for historical reasons.

Be aware that the ATTRIBUTE_NODE is being deprecated in DOM4.

Subnode Objects Inherit From the Node Object

  • Each node object in a typical DOM tree inherits properties and methods from Node.

Examples

Object < Node < Element < HTMLElement < (e.g., HTML*Element)
Object < Node < Attr (this is deprecated in DOM4)
Object < Node < CharacterData < Text
Object < Node < Document < HTMLDocument
Object < Node < DocumentFragment

< indicates “inherited from”

Properties and Methods for Working with Nodes

  • As we have been discussing, all node objects (e.g., Element, Attr, Text, and so on) inherit properties and methods from a primary Node object.
  • These properties and methods are the baseline values and functions for manipulating, inspecting, and traversing the DOM.
  • Node properties
    • childNodes
    • firstChild
    • lastChild
    • nextSibling
    • nodeName
    • nodeType
    • nodeValue
    • parentNode
    • previousSibling
  • Node methods
    • appendChild()
    • cloneNode()
    • compareDocumentPosition()
    • contains()
    • hasChildNodes()
    • insertBefore()
    • isEqualNode()
    • removeChild()
    • replaceChild()
  • HTML*Element properties
    • innerHTML
    • outerHTML
    • textContent
    • innerText
    • outerText
    • firstElementChild
    • lastElementChild
    • nextElementChild
    • children

Identifying the Type and Name of a Node

  • Every node has a nodeType and nodeName property that is inherited from Node.
  • For example, Text nodes have a nodeType code of 3 and a nodeName value of #text
document.doctype.nodeName
// "html" 
document.doctype
// <!DOCTYPE html>
document.doctype.nodeType
// 10
document.querySelector('a')
// <a ../>
document.querySelector('a').nodeName
// "A"
document.querySelector('a').nodeType
// 1

Selecting Element

The document comes with a bunch of methods for selecting elements. 

const tag = document.getElementById("highlight");
const tags = document.getElementsByClassName("bolded");

<body>
  <h1>Hello</h1>
  <h1>Goodbye</h1>
  <ul>
    <li id="highlight">List Item 1</li>
    <li class="bolded">List Item 2</li>
    <li class="bolded">List Item 3</li>
  </ul>
</body>

document.getElementById()

  • Takes a string argument and returns the one element with a matching ID

document.getElementsByClassName()

  • Takes a string argument and returns a list of elements that have a matching class

document.querySelector()

  • Returns the first element that matches a given CSS-style selector

document.getElementsByTagName()

  • Returns a list of all elements of a given tag name, like <li> or <h1>
const tags = document.getElementsByTagName("li");
const tags = document.getElementsByTagName("h1");
const tag = document.querySelector(".bolded");
const tags = document.querySelectorAll("h1");

<body>
  <h1>Hello</h1>
  <h1>Goodbye</h1>
  <ul>
    <li id="highlight">List Item 1</li>
    <li class="bolded">List Item 2</li>
    <li class="bolded">List Item 3</li>
  </ul>
</body>

document.querySelectorAll()

  • Returns a list of elements that matches a given CSS-style selector

Node Collections (NodeList / HTMLCollection)

  • When selecting groups of nodes or accessing predefined sets of nodes, the nodes are placed in either a NodeList [e.g., document.querySelectorAll('*')] or an HTMLCollection (e.g., document.scripts).
  • These array-like object collections have the following characteristics:
    • ​A collection can be either live or static.
    • By default, nodes are sorted inside the collection by tree order. This means the order matches the linear path from tree trunk to branches.
    • The collections have a length property that reflects the number of elements

createElement() and createTextNode()

Creating Element

const elementNode = document.createElement('div');
const textNode = document.createTextNode('Hi');
<!DOCTYPE html>
<html lang="en">
<body>
<div id="A"></div>
<span id="B"></span>
<div id="C"></div>
<div id="D"></div>
<div id="E"></div>
</body>
</html>
//create a strong element and text node and add it to the DOM
document.getElementById('A').innerHTML = '<strong>Hi</strong>';
/* create a div element and text node to replace <span id="B"></div>
(notice span#B is replaced) */
document.getElementById('B').outerHTML = '<div id="B"
 class="new">Whats Shaking</div>'
//create a text node and update the div#C with the text node
document.getElementById('C').textContent = 'dude';
//NON standard extensions below i.e., innerText and outerText
//create a text node and update the div#D with the text node
document.getElementById('D').innerText = 'Keep it';
/* create a text node and replace the div#E with the text node
(notice div#E is gone) */
document.getElementById('E').outerText = 'real!'; //non-standard property

console.log(document.body.innerHTML);
/* logs
<div id="A"><strong>Hi</strong></div>
<div id="B" class="new">Whats Shaking</div>
<span id="C">dude</span>
<div id="D">Keep it</div>
real!
*/

Add Node Objects to the DOM

 appendChild() and insertBefore()

The appendChild() method will append a node (or multiple nodes) to the end of the child node(s) of the node the method is called on.

//create a blink element node and text node
var elementNode = document.createElement('strong');
var textNode = document.createTextNode(' Dude');
//append these nodes to the DOM
document.querySelector('p')
    .appendChild(elementNode);
document.querySelector('strong')
    .appendChild(textNode);
//log's <p>Hi<strong> Dude</strong></p>

The insertBefore() method requires two parameters: the node to be inserted and the reference node in the document before which you would like the node inserted.

//create a text node and li element
// node and append the text to the li
var text1 = document.createTextNode('1');
var li = document.createElement('li');
li.appendChild(text1);
//select the ul in the document
var ul = document.querySelector('ul');
/*
add the li element we created 
above to the DOM, notice I call on <ul>
and pass reference to <li>2</li> 
using ul.firstChild
*/
ul.insertBefore(li,ul.firstChild);
console.log(document.body.innerHTML);
/*logs
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
*/

Remove and Replace Nodes

removeChild() and replaceChild()

  • Removing a node from the DOM is a bit of a multistep process.
  • First you have to select the node you want to remove.
  • Then you need to gain access to its parent element, typically by using the parentNode property.
  • It’s on the parent node that you invoke the removeChild() method, passing it the reference to the node to be removed.
//remove element node
var divA = document.getElementById('A');
divA.parentNode.removeChild(divA);

//remove text node
var divB = document.getElementById('B').firstChild;
divB.parentNode.removeChild(divB);
<div id="A">Hi</div>
<div id="B">Dude</div>

Inline CSS Properties

  • Inline CSS styles are individually represented as a property (i.e., an object property) of the style object available on element node objects.
  • This provides the interface for us to get, set, or remove individual CSS properties on an element by simply setting an object’s property value.
//set
divStyle.backgroundColor = 'red';
divStyle.border = '1px solid black';
divStyle.width = '100px';
divStyle.height = '100px';

const divStyle = document.querySelector('div').style;
// Remove
divStyle.backgroundColor = '';
divStyle.border = '';
divStyle.width = '';
divStyle.height = '';
//get
console.log(divStyle.backgroundColor);
console.log(divStyle.border);
console.log(divStyle.width);
console.log(divStyle.height);

Can also use - 

setProperty(propertyName, value)

removeProperty()

divStyle.setProperty('background-color','red');
divStyle.setProperty('border','1px solid black');

getProperty(propertyName)

divStyle.getPropertyValue('background-color');
divStyle.getPropertyValue('border');

cssText

divStyle.removeProperty('background-color');
divStyle.removeProperty('border');
divStyle.cssText = 'background-color:red;
        border:1px solid black;
        height:100px;
        width:100px;';

getComputedStyle()

To get an element’s CSS from the cascade (i.e., cascading from inline stylesheets, external stylesheets, and browser stylesheets) as well as its inline styles.

DOM Events

  • Events are everywhere
  • An event, in terms of the DOM, is either a predefined or a custom moment in time that occurs in relationship to an element in the DOM, the document object, or the window object.
  • These moments can be initiated by the state of the UI
    • input is focused or something has been dragged
  • the state of the environment running the JS program
    • a page is loaded or an XHR request has finished
  • or the state of the program itself
    •  monitor all mouse clicks for 30 seconds after the page has loaded).
  • Dragging and Dropping
  • Pressing the Enter key
  • Clicking on a button
  • Hovering over a link

Some Event Types

  • User interface events
    • load, unload
  • Focus events
    • blur, focus
  • Form events
    • change, reset, submit
  • Mouse events
    • click, dblclick,
      mousedown
  • Wheel event
    • mousewheel
  • Keyboard events
    • keypress, keydown
  • Touch events
    • touchstart, touchend
  • Window, <body> / frame-specific events
    • afterprint, beforeunload
  • Document-specific events
    • readystatechange, DOMContentLoaded
  • Drag events
    • drag, dragstart, dragend, drop

Setting Up Event Handles

Setting up events can be accomplished using

<body onclick="console.log('fire/trigger attribute event handler')">
var elementDiv = document.querySelector('div');
elementDiv.onclick = function() {
    console.log('fire/trigger property event handler');
};
elementDiv.addEventListener('click',function() {
    console.log('fire/trigger addEventListener')
}, false);

only the addEventListener() provides a robust and organized solution.

inline attribute event handlers

property event handlers

addEventListener() method.

Removing Event Handlers

The removeEventListener() method can be used to remove event listeners, if the original listener was not added using an anonymous function

var sayHi = function(){console.log('hi')};

//adding event listener using anonymous function
document.body.addEventListener('click',function(){
    console.log('dude');
},false);

document.querySelector('div').removeEventListener('click',sayHi,false);

//adding event listener using function reference
document.querySelector('div').addEventListener('click',sayHi,false);

Anonymous functions added using the addEventListener() method simply cannot be removed.

Event Flow

Event objects are dispatched to an event target. But before dispatch can begin, the event object’s propagation path must first be determined.

  • The propagation path is an ordered list of current event targets through which the event passes.
  • Once the propagation path has been determined, the event object passes through one or more event phases. There are three event phases: capture phase, target phase and bubble phase
  • The capture phase: The event object propagates through the target’s ancestors from the Window to the target’s parent. This phase is also known as the capturing phase.
  • The target phase: The event object arrives at the event object’s event target. This phase is also known as the at-target phase. If the event type indicates that the event doesn’t bubble, then the event object will halt after completion of this phase.
  • The bubble phase: The event object propagates through the target’s ancestors in reverse order, starting with the target’s parent and ending with the Window. This phase is also known as the bubbling phase.

Event Object

By default, the handler or callback function invoked for events is sent a parameter that contains all relevant information about the event itself

document.querySelector('div').addEventListener('click', function(event) {
    Object.keys(event).sort().forEach(function(item){
     console.log(item+' = '+event[item]); //logs event properties and values
    });
},false);

preventDefault()

  • Browsers provide several events already wired up when an HTML page is presented to a user.
  • These browser events can be prevented by calling the preventDefault() method inside the event handler function associated with a node or object that invokes a browser default event.

 stopPropagation()

  • Calling stopPropagation() from within an event handler/listener will stop the capture and bubble event flow phases, but any events directly attached to the node or object will still be invoked.

Event Delegation

  • Event delegation is the programmatic act of leveraging the event flow and a single event listener to deal with multiple event targets.
  • A side effect of event delegation is that the event targets don’t have to be in the DOM when the event is created in order for the targets to respond to the event.
<table border="1">
 <tbody>
 <tr><td>row 1 column 1</td><td>row 1 column 2</td></tr>
 <tr><td>row 2 column 1</td><td>row 2 column 2</td></tr>
 <tr><td>row 3 column 1</td><td>row 3 column 2</td></tr>
 <tr><td>row 4 column 1</td><td>row 4 column 2</td></tr>
 <tr><td>row 5 column 1</td><td>row 5 column 2</td></tr>
 <tr><td>row 6 column 1</td><td>row 6 column 2</td></tr>
 </tbody>
</table>
document.querySelector('table').addEventListener('click', function(event) {
 if(event.target.tagName.toLowerCase() === 'td'){ 
/* make sure we only run code if a td is the target */
 console.log(event.target.textContent); /* use event.target to gain access
 to target of the event which is
 the td */
 }
},false); 

DOM

By Arfat Salman