Jump to main content
Menu

DOM Fundamentals and Locating Element Nodes

Introducing the DOM

  • The Document Object Model (DOM) is an Application Programming Interface (API) that represents the web page content as a tree structure (a hierarchy) of nodes.
  • Over the course of its development, the World Wide Web Consortium (W3C) and the Web Hypertext Application Technology Working Group (WHATWG) have both worked on different versions of the specification.
  • The power of the DOM is that it allows us to access and update any element, attribute, or piece of text in the web page.
  • The DOM is also platform-independent and language-independent; JavaScript is only one of many languages that can use the DOM to access the structure and content of a document.
  • Prior to the W3C and WHATWG taking up the DOM specification process, there were 3 more primitive DOMs that browser manufacturers created:
    • The very first, DOM0, was introduced with early versions of the Netscape browser. DOM0 was never standardized and successive generations of web browsers (including current ones) simply copied what those early Netscape browsers supported. document.write() is a DOM0 method.
    • Internet Explorer 4 introduced the document.all DOM.
    • Netscape 4 introduced the document.layers DOM.
    • If you encounter those in a production website, you're looking at some very old code indeed.

Types of Nodes & Node Relationships

  • The content of a web page (elements, attributes, and text) are represented as a hierarchy of nodes, one node for each as they occur in the document.
  • A simple example is:
    <li class="navitem">Nav Item 1</li>
    
  • In the example we have one node of each type:
    • Element Node: <li></li>
    • Attribute Node: class="navitem"
    • Text Node: Nav Item 1
  • These nodes represent parent-child relationships:
    • The <li></li> element node is a parent to the text node.
    • The text node is a child of the <li></li> element node.
  • Element nodes can be parents and children.
  • Text nodes can only be children; they cannot be parents.
  • Attribute nodes are not part of the document tree (the node hierarchy) and as a result:
    • Attributes cannot be parent or child nodes.
    • Attribute nodes exist only as properties of the element node to which they belong.
    • If you want to get at an attribute node you need to pinpoint the element node to which it belongs.
  • Newlines in your code are treated as whitespace nodes, which are empty text nodes.
  • Content that we generate and put in the page via DOM methods will not have any whitespace in it. This is a good thing.

Timing & Sequencing

  • Due to the interpreted nature of JavaScript, where code is placed and when it runs plays a critical role in whether your script works or immediately errors out.
  • When working with the DOM the HTML structural code needs to be parsed before the JavaScript can work with it.

Five Approaches to Timing & Sequencing

  • 'load' event on the window object
    • The load event fires once the HTML, CSS, and all other text and binary data (e.g., images) have loaded.
    • If the JavaScript that interacts with the DOM runs after load then the browser is aware of all the nodes you are trying to acquire.
    • Some sample code:
      window.addEventListener('load', functionReference, false);
      
    • This can go anywhere in your .js file; it does not execute until every other piece of code has been rendered / parsed so there is no sequencing concern.
    • The drawback of this approach is the delay for the JavaScript to run, which varies based on what your page contains.
    • This is especially noticeable for pages with lots of images that take a while to download. It can be jarring when the JavaScript finally loads, if it alters the visual appearance of the page.
    • This approach allows you to put the <script></script> tags in the <head></head> region, which makes maintenance easier (there is no guessing where the tags are).
  • Placing <script></script> tags just before </body>
    • By placing these just before </body> you ensure that all the HTML and CSS have been parsed first and you do not need the 'load' event called on window.
    • The advantage of this approach is faster performance; your JavaScript is executing as the images and other media are downloading.
  • Checking for the desired nodes periodically
    • In this approach you use window timing methods such as setTimeout() and setInterval() to periodically see if the node you need to manipulate has been loaded yet.
    • Your code can be called from <head></head> and does not need the 'load' event on window.
    • This approach can yield the fastest results, but it adds a layer of complexity that is best avoided unless you are struggling with performance and really need to speed things up.
  • DOMContentLoaded event
    • The DOMContentLoaded event fires when the HTML and CSS have loaded but before the binary data (e.g., images) have loaded.
    • Some sample code:
      window.addEventListener('DOMContentLoaded', functionReference, false);
      
    • This code can be called from <head></head>
  • Specifying the defer attribute in your script tag
    • This attribute delays execution of your JavaScript until just before DOMContentLoaded fires.
    • This can only be used with external JavaScript files (not embedded JavaScript).
    • Sequencing of files is maintained, so if you have multiple script tags with defer specified, they will execute in the order they appear in the HTML. This is essential to avoid breakage when there are dependencies.
    • The script tag can be located in <head></head> and will not block other downloads, because the browser knows not to hold things up.

Choosing an Approach

Any of these 3 options are fine:

  • Placing <script></script> tags just before </body>
  • DOMContentLoaded event
  • Specifying the defer attribute in your script tag

Event Handlers

  • The addEventListener method is used to associate events with element nodes (in order words, HTML tags) in the DOM, as well as with the window object.
  • Learn more about the addEventListener method, as well as many more details about what is happening in event listening.
  • This article will briefly show addEventListener, with some examples showing it in action.
  • One of the most common accessibility downfalls in JavaScript is ignoring keyboard events and focusing only on mouse events. Users without a mouse, or unable to use one, will not be able to trigger the JavaScript.
  • The solution is to use both mouse and keyboard event handlers in your scripts. How you structure your HTML also makes a major impact on whether keyboard events can be used (links and form elements can be tabbed to without any extra development effort, so they make it possible to use the keyboard to access them).
  • A full list of event handlers is available on MDN; we will address some of the more commonly encountered ones in this lecture.
Event Device Triggers Used For Considerations
click mouse, keyboard When the element is clicked (mouse), or tabbed to and the Enter key is pressed (keyboard) Anything involving clicking a button or link This is one of the most freqently used event handlers.
mouseenter mouse When the element is moused over Navigation menus, among other things Preferable to mouseover because tag nesting will not cause unwanted event firing. This is because the event does not bubble - see the event listening article for details.
mouseleave mouse When the element is moused away from Navigation menus, among other things Preferable to mouseout because tag nesting will not cause unwanted event firing. This is because the event does not bubble - see the event listening article for details.
mousedown mouse When the mouse button is pressed down Drag-and-drop scripts Useful primarily for drag-and-drop functionality. There is no keyboard equivalent.
mouseup mouse When the mouse button is released Drag-and-drop scripts Useful primarily for drag-and-drop functionality. There is no keyboard equivalent.
mousemove mouse When the mouse is moved Drag-and-drop scripts Useful primarily for drag-and-drop functionality. This will trigger repeatedly as the user mouses around, so a debounce function to limit the number of function executions is recommended. There is no keyboard equivalent.
focus mouse, keyboard When a link, form element, or window gains focus Form element data validation, among other things This is the keyboard equivalent to mouseenter.
blur mouse, keyboard When a link, form element, or window loses focus Form element data validation, among other things This is the keyboard equivalent to mouseleave.
keydown keyboard When a key is pressed; typically the user is typing in a form field Notifying the user how many characters they have entered out of the amount allowed, among other things This event fires before the character is added to the form field. The event will keep firing (frequency is based on the keyboard sensitivity the user has set in their operating system) as long as the key is held down.
keyup keyboard When a key is released; typically the user is typing in a form field Notifying the user how many characters they have entered out of the amount allowed, among other things This event fires after the character is added to the form field.
input keyboard, mouse When the user moves between options in a select menu (for example) Choosing an option may cause something else in the form to appear, to be disabled, etc. This event fires on input, select, and textareas when their value changes.
submit keyboard, mouse When the user submits the form Validating form data This listener must be applied to the form element node (not to a button element node). If JavaScript submits the form this does not trigger; it only responds to the user activating (via clicking or tabbing and pressing Enter) on a type="submit" button.

Selecting Nodes

These methods return either a single element node reference, a node list or HTML collection (which contains 1 or more references to element nodes), or either undefined or null (if nothing is matched).

  • Node lists and HTML collections have a length property, like arrays. They are always indexed by number, starting with 0.
  • These are not array objects, so avoid using array methods (they will fail).
  • For our purposes, you can treat node lists and HTML collections as interchangeable.
Method Called From What is Passed Returns Notes
getElementById() document id attribute value (e.g., "mainContent") A single element node reference or null Remember to leave off the # sign before the id value. You are not passing a CSS selector.
getElementsByTagName() An element node or document (if entire DOM should be looked through) Tag name (e.g., "li") Dynamic node list This is a live/dynamic node list, which means it updates when the DOM changes. Being able to call this from another element node also provides flexibility and narrows where the matching is happening.

If nothing is matched, the node list has a length of 0.

getElementsByName() document name attribute value (e.g., "firstname") Dynamic node list Primarily useful for form elements, as name attributes are seldom found outside form elements. And likely most helpful with radio buttons or checkboxes, as groups of radio buttons share the same name value.

If nothing is matched, the node list has a length of 0.

In some browsers, elements added after page load may be omitted when this method is used. This seems like a bug, or perhaps those browsers viewing this as a static node list rather than a dynamic node list.

getElementsByClassName() An element node or document (if entire DOM should be included) Class attribute value (e.g., "primaryHeader") Dynamic HTML collection This is a live collection of element node references, which means it updates when the DOM changes. Being able to call this from another element node also provides flexibility.
querySelector() An element node or document (if entire DOM should be included) CSS selector (e.g., "#mainContent .primaryHeader") A single element node reference (the first instance in the DOM) or null (if not found). The browser has to support the CSS selector used. Significant flexibility.
querySelectorAll() An element node or document (if entire DOM should be included) CSS selector (e.g., "#mainContent li") Static node list If nothing is matched, you still have a node list returned. It just has a length of 0.

Unlike the other methods that return node lists, the returned node list in this case is non-live or static. If you have changed the DOM after calling this method you need to rerun the method to pick up the changes.

Selector Code Examples

document.getElementById() Example

See the Pen getElementById by Jason Withrow (@jwithrow) on CodePen.

document.getElementsByName() Example

See the Pen getElementsByName by Jason Withrow (@jwithrow) on CodePen.

document.getElementsByTagName() Example

See the Pen getElementsByTagName by Jason Withrow (@jwithrow) on CodePen.

document.getElementsByClassName() Example

See the Pen getElementsByClassName by Jason Withrow (@jwithrow) on CodePen.

Selectors API (document.querySelector and document.querySelectorAll) Example

See the Pen Selectors API by Jason Withrow (@jwithrow) on CodePen.

To see the console messages, edit the example on CodePen and select the 'Console' button in the lower left corner.