CSE 154

Lecture 8: DOM Manipulation

Administrivia

HW2 Part A is out now, due Saturday (no late days).

HW2 Part B will go out later this week, due next Wednesday.

A and B are not equal parts! B is much larger.

Handy JS cheat sheet here

JavaScript

It's a new language for a lot of folks here.

It's okay that it'll take some time to get used to.

Imagine: You know English, but you're starting to learn... German. Many words are going to sound the same (e.g., Wetter vs. Weather*), but many other things are different.

It's not going to come naturally right away, but the more languages you know and learn, the easier the next one will be.

* Note: I don't speak German.

Module 2 Learning Objectives

JS Basics (Strings, Numbers, Arrays, Functions, etc.)

Basic HTML UI Elements (buttons, text input, select dropdowns, etc.)

The DOM

  • Node Access (getElementById, querySelector, querySelectorAll)
  • Tree Manipulation (appendChild, removeChild, textContent, etc.)
  • Style Manipulation (style vs. classList)

Events

  • Common page/user events: load, click, change, mouseover, etc
  • DOM/event connections: addEventListener and removeEventListener
  • Delayed and repeated events: setTimeout and setInterval (Friday)

Recall: Accessing DOM objects

  1. Ask for them by id: document.getElementyById(...)
  2. Query for them with CSS style selectors:
    • document.querySelector(...)
    • document.querySelectorAll(...)

We will use the id, qs, and qsa shorthand functions often

More about document.querySelectorAll

Back to an earlier lecture...

The Document Object Model

DOM Tree representation

How the browser represents a page

Very useful when thinking about selectors!

DOM and Selectors: Q2

DOM Tree representation

How to select the colored elements in CSS?

Using a class selector: .column

Using a combinator selector: #container > div

What about in JS?

document.querySelectorAll(".column") (or with qsa(".column")

qsa("#container > div")

Examples in a Skittles Jar

let gameColor = document.getElementById("color");
let skittles = document.querySelectorAll(".skittle");
let greenSkittles = document.querySelectorAll(".green.skittle");
let greenSkittleCount = greenSkittles.length;

let firstSkittle = document.querySelector(".skittle");

JS

Try these commands in the Chrome Console on the linked HTML page!

Common querySelectorAll issues

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

// get all elements with class of ".skittle"
let skittles = document.querySelectorAll("skittle");
let skittles = document.querySelectorAll(".skittle");

JS

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

// hide all skittles
qsa(".skittle").classList.add("hidden");
let skittles = document.querySelectorAll(".skittle");
for (let i = 0; i < skittles.length; i++) {
  skittle.addEventListener("click", eatSkittle);
}

JS

Handy Shortcut Functions

We will use document.getElementById and document.querySelectorAll a LOT. It's handy to declare a shortcut to help us out. You may use the following in your JS functions (these are exceptions to the rule of having description function names).

/**
 * Returns the element that has the ID attribute with the specified value.
 * @param {string} idName - element ID
 * @returns {object} DOM object associated with id.
 */
function id(idName) {
  return document.getElementById(idName);
}

/**
 * Returns the array of elements that match the given CSS selector.
 * @param {string} selector - CSS query selector
 * @returns {object[]} array of DOM objects matching the query.
 */
function qsa(selector) {
  return document.querySelectorAll(selector);
}

/**
 * Returns the first element that matches the given CSS selector.
 * @param {string} selector - CSS query selector.
 * @returns {object} The first DOM object matching the query.
 */
function qs(selector) { // less common, but you may find it helpful
  return document.querySelector(selector);
}

JS

Modifying the classList

You can manipulate the DOM element's classList with the following methods:

Name Description
add(classname) Adds the specified class(es) to the list of classes on this element. Any that are already in the classList are ignored.
remove(classname) Removes the specified class(es) to the list of classes from this element. Any that are already not in the classList are ignored without an error
toggle(classname) Removes a class that is in the list, adds a class that is not in the list.
contains(classname) Returns true if the class is in the the DOM element's classList, false if not.
replace(oldclass, newclass) Replaces the old class with the new class.

Skittles

Today, we'll review how to access DOM elements and introduce how to add new ones this Skittles page

running version of lec09 skittles

Starter JS: skittles-starter.js (skittles-lec09-starter.zip)

Part 1/2: Setting up the Page

"use strict";
(function() {

  window.addEventListener("load", init);

  function init() {
    id("answer-btn").addEventListener("click", showAnswer);
    // 2.1. When #start-btn is clicked, fillJar should be called.
    id("start-btn", fillJar);
  }
  ...
})();

JS

Recap of Event handlers

Passing a function, vs passing the result of a function

addEventListener("click", openBox);

vs.

addEventListener("click", openBox());

Part 3: Showing the Answer

When the #answer-btn is clicked, we want to populate the #count span with the number of green skittles currently in the jar.

<article id="game-play">
  <button id="answer-btn">Give me the answer!</button>
  <p class="hidden">
    There are <span id="count"></span> skittles in the jar!
  </p>
</article>

skittles-jar.html

...
// Part 3: Called when #answer-btn is clicked (see init())
function showAnswer() {
  let greenSkittles = qsa(".green.skittle");
  id("count").textContent = greenSkittles.length; // e.g., 17
  // show the paragraph (remember that .hidden is implemented in CSS)
  qs("#game-play p").classList.remove("hidden");
}

skittles.js

But The Internet™ says...

You might see innerHTML and textContent used interchangeably.

We prefer textContent

  • Not modular: HTML code embedded within JS
  • What if you have a complicated new node (with many subchildren) to add?
  • Error-prone: must carefully distinguish " and '
  • Can only add at beginning or end, not in middle of child list

Part 4: Getting closer to "Starting" a Game

When the Start Button is clicked, how would we hide/show views?

How would we fill the jar with a "test skittle"?

DOM Manipulation

How do we create new DOM elements?

How do add them to existing DOM elements?

Creating New Node Objects

Name Description
document.createElement("tag") creates and returns a new empty DOM node representing an element of that type
// create a new <h2> node
let newHeading = document.createElement("h2");
newHeading.textContent = "This is a new heading!";

JS

// create a new <div> node
let skittle = document.createElement("div");
skittle.classList.add("skittle");
skittle.classList.add("green");
// or, skittle.classList.add("skittle", "green");

JS

Note: 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...

An Aside

One more alias function!

When creating new DOM elements using JS, you may use document.createElement often.

We have added one more alias function, gen to include with id, qs, and qsa.

function gen(tagName) {
return document.createElement(tagname);
}

JS

Adding/Removing Nodes to the DOM

When you have a parent DOM node, you can add or remove a child DOM node using the following functions:

Name Description
parentNode.appendChild(node) places the given node at end of this node's child list
parentNode.insertBefore(new, old) places the given node in this node's child list just before old child
parentNode.removeChild(node) removes the given node from this node's child list
node.remove() removes the node from the page
parentNode.replaceChild(new, old) replaces given child with new nodes
let li = document.createElement("li");
li.textContent = "A list item!";
id("my-list").appendChild(li);

JS

Part 4: Adding Skittles to the Jar

// ... called when #start-btn is clicked
function fillJar() {
  let skittle = document.createElement("div");
  skittle.classList.add("skittle");
  skittle.classList.add("green");
  id("jar").appendChild(skittle); // add the new skittle to the jar!
}

skittles.js

Part 4 V2: Using our randomColor function

What if instead of a green skittle, we want a randomly-colored skittle?

// ... called when #start-btn is clicked
function fillJar() {
  // for now, we just add a "test" skittle
  let skittle = document.createElement("div");
  skittle.classList.add("skittle");
  //skittle.classList.add("green");
  skittle.classList.add(getRandomColor());
  id("jar").appendChild(skittle);
}

// Implemented in Part 5
function getRandomColor() {
  const COLORS = ["red", "green", "blue"];
  let randomIndex = Math.floor(Math.random() * COLORS.length);
  return COLORS[randomIndex];
}

skittles.js

What if we wanted to clear the jar?

Three methods for removing elements

Get all of the DOM elements and remove them from the DOM

function clearJar() {
  let skittles = qsa(".skittle");
  for (let i = 0; i < skittles.length; i++) {
    // 1. Using node.remove();
    skittles[i].remove();
    // 2. Using parentNode.removeChild(node);
    // skittles[i].parentNode.removeChild(skittles[i]);
  }
}

JS

Or ... Method 3: Set the Jar's innerHTML to be empty!

function clearJar() {
  id("jar").innerHTML = "";
}

JS