CSE 154

Lecture 9: The DOM Tree

The DOM

The Keyword this


          this.fieldName                 // access field
          this.fieldName = value;        // modify field
          this.functionName(parameters); // call method
          

JS

All JavaScript code actually runs inside of an object

By default, code runs in the global window object (so this === window)

  • All global variables and functions you declare become part of window

The this keyword refers to the current object

Event Handler Binding


          window.onload = function() {
            document.getElementById("textbox").onmouseout = booyah;
          };

          function booyah() { // booyah knows what object it was called on
            this.value = "booyah";
          }
          

JS

output

Event handlers attached unobtrusively are bound to the element

Inside the handler, that element becomes this

The DOM Tree

the DOM hierarchy

The elements of a page are nested into a tree-like structure of objects

The DOM has properties and methods for traversing this tree

DOM Versus innerHTML Hacking

Why not just code this way?


          function slideClick() {
            document.getElementById("main").innerHTML += "<p>A paragraph!</p>";
          }
          

JS

Imagine that the new node is more complex:

  • Ugly: bad style on many levels (e.g., JS code embedded within HTML)
  • Error-prone: must carefully distinguish " and '
  • Can only add at beginning or end, not in middle of child list

          function slideClick() {
            document.getElementById("main").innerHTML += <p style='color: red; " +
              "margin-left: 50px;' " + "onclick='myOnClick();'>" +
              "A paragraph!</p>";
          }
          

JS

Creating New Nodes


          // create a new <h2> node
          let newHeading = document.createElement("h2");
          newHeading.innerHTML = "This is a heading";
          newHeading.style.color = "green";
          

JS

Name Description
document.createElement("tag") creates and returns a new empty DOM node representing an element of that type
document.createTextNode("text") creates and returns a text node containing given text

Merely creating an element does not add it to the page

You must add the new element as a child of an existing element on the page...

Modifying the DOM Tree


          let p = document.createElement("p");
          p.innerHTML = "A paragraph!";
          document.getElementById("main").appendChild(p);
          

JS

Every DOM element object has these methods:

Name Description
appendChild(node) places the given node at end of this node's child list
insertBefore(new, old) places the given node in this node's child list just before old child
removeChild(node) removes the given node from this node's child list
replaceChild(new, old) replaces given child with new nodes

Complex DOM Manipulation Problems

How would we do each of the following in JavaScript code? Each involves modifying each one of a group of elements...

  • When the Go button is clicked, reposition all the divs of class 'puzzle' to random x/y locations
  • When the user hovers over the maze boundary, turn all maze walls red
  • Change every other item in the ul list with id of 'tas' to have a gray background

Selecting Groups of DOM Objects

Methods in document and other DOM objects:

Name Description
getElementsByTagName(tag) returns array of descendents with the given tag, such as "div"
getElementsByName(name) returns array of descendents with the specified name (mostly useful for accessing form controls)
querySelector(selector) returns the first element that would be matched by the given CSS selector string
querySelectorAll(selector) returns an array of all elements that would be matched by the given CSS selector string

Getting All Elements of a Certain Type

Highlights all paragraphs in the document:


          let allParas = document.querySelectorAll("p");
          for (let i = 0; i < allParas.length; i++) {
            allParas[i].style.backgroundColor = "yellow";
          }
          

JS


          <body>
            <p>This is the first paragraph</p>
            <p>This is the second paragraph</p>
            <p>You get the idea...</p>
          </body>
          

HTML

This is the first paragraph

This is the second paragraph

You get the idea...

output

Complex Selectors

Highlight all paragraphs inside of the section with ID 'address':


          let addrParas = document.querySelectorAll("#address p");
          for (let i = 0; i < addrParas.length; i++) {
            addrParas[i].style.backgroundColor = "yellow";
          }
          

JS


          
            <p>This won't be highlighted!</p>
            <div id="address>
              <p>1234 Street</p>
              <p>Seattle, WA</p>
            </div>
            
          
          

HTML

This won't be highlighted!

1234 Street

Seattle, WA

output

Common querySelectorAll Issues

Many students forget to write . or # in front of a class or id


          // get all buttons with a class of "control"
          let gameButtons = document.querySelectorAll("control");
          let gameButtons = document.querySelectorAll(".control");
          

JS

querySelectorAll returns an array, not just a single element; must loop over the results

  • document.querySelector returns just the first element that matches, if that's what you want

          // set all buttons with a class of "control" to have red text
          document.querySelectorAll(".gamebutton").style.color = "red";
          let gameButtons = document.querySelectorAll(".gamebutton");
          for (let i = 0; i < gameButtons.length; i++) {
            gameButtons[i].style.color = "red";
          }
          

JS

Q: Can I still select a group of elements using querySelectorAll even if my CSS file doesn't have any style rule for that same group? (A: Yes!)

Problems with Reading/Changing Styles


            
          

HTML


          window.onload = function() {
            document.getElementById("clickme").onclick = biggerFont;
          };

          function biggerFont() {
            let button = document.getElementById("clickme");
            let size = parseInt(button.style.fontSize);
            button.style.fontSize = (size + 4) + "pt";
          }
          

JS

output

The style property lets you set any CSS style for an element

A catch: you can only use this to read styles set in the html or with the DOM .style. You cannot read css properties from this.

Accessing Elements' Existing Styles


          window.getComputedStyle(element).propertyName;
          

JS (template)


          function biggerFont2() {
            let button = document.getElementById("clickme2");
            let size = parseInt(window.getComputedStyle(button).fontSize);
            button.style.fontSize = (size + 4) + "pt";
          }
          

JS (example)

output

getComputedStyle method of global window object accesses existing styles

Common Bug: Incorrect Usage of Existing Styles

The following example attempts to add 100px to the top of main, but fails.

Consider the case when main has top set to "200px". Then this code would update style.top to be the invalid value of "200px100px"


          let main = document.getElementById("main");
          main.style.top = window.getComputedStyle(main).top + 100 + "px";
          

JS

A corrected version:


          main.style.top = parseInt(window.getComputedStyle(main).top) + 100 + "px";
          

JS

Getting/Setting CSS Classes


          function highlightField() {
            // turn text yellow
            let text = document.getElementById("text");
            if (!text.className) {
              text.className = "highlight";
            } else if (text.className.indexOf("invalid") < 0) {
              text.className += "highlight"; // awkward
            }
          }
          

JS

JS DOM's className property corresponds to HTML class attribute

Somewhat clunky when dealing with multiple space-separated classes as one big string

Getting/Setting CSS Classes with classList


          function highlightField() {
            // turn text yellow
            let text = document.getElementById("text");
            if (!text.classList.contains("invalid")) {
              text.classList.add("highlight");
            }
          }
          

JS

classList collection has methods add, remove, contains, and toggle to manipulate CSS classes

Similar to existing className DOM property, but don't have to manually split by spaces

Removing a Node From the Page


          function slideClick() {
            let bullet = document.getElementById("removeme");
            bullet.parentNode.removeChild(bullet);
          }
          

JS

An odd idiom: obj.parentNode.remove(obj)

Types of DOM Nodes


            

This is a paragraph of text with a link in it.

HTML

Node Types

Element Nodes (HTML tag)

  • Can have children and/or attributes

Text Nodes (text in a block element)

Attribute Nodes (attribute/value pair)

  • text/attributes are children in an element node
  • Cannot have children and/or attributes
  • Not usually shown when drawing the DOM tree

Traversing the DOM Tree Manually

Every node's DOM object has the following properties:

Name(s) Description
firstChild, lastChild start/end of this node's list of children
childNodes array of all of this node's children
nextSibling, previousSibling neighboring nodes with the same parent
parentNode the element that contains this node

Complete list of DOM node properties

Browser incompatibility information (IE6 sucks)

DOM Tree Traversal Example


                

This is a paragraph of text with a link.

HTML

DOM traversal example

Element vs. Text Nodes


          

This is a paragraph of text with a link.

HTML

Q: How many children does the div have?

A: 3

  • an element node representing the <p>
  • two text nodes representing "\n\t" (before/after the paragraph)

Q: How many children does the paragraph have? The a tag?