Lecture 3 - JS and the DOM
The Document Object Model (DOM)
A web page (or document) is a set of (many) nested boxes, i.e. nested elements. The DOM is a tree data structure representing the nested structure of the document. The boxes (HTML tags in our context) are nodes in the tree. The DOM properties and methods (the API) provide programmatic access to the tree to access or change the document's structure, style or content.
More specifically JavaScript can:
- Change all the HTML elements in the page
- Change all the HTML attributes in the page
- Change all the CSS styles in the page
- Remove existing HTML elements and attributes
- Add new HTML elements and attributes
- React to all existing HTML events in the page
- Create new HTML events in the page
The root of this tree is Document. It's child is the html
tag, whose children
are the head
and body
(thus head
and body
are siblings), etc. all the
way down the leaf nodes containing text (or childless HTML elements). We can
programmatically
navigate the DOM
using tree concepts like parent, children, etc.
As an example, navigate to the course webpage and open the Firefox web console. Here we can
experiment "live" with the DOM API... Try some of the methods from the links
above, like document.children
. For more detail about the tree-based API,
review the Node,
ParentNode and
ChildNode
interfaces.
Some examples:
> document
Some built-in properties of document
:
> document.head
> document.body
Alternately we can explore via the "tree" API, e.g.
> document.children // only available on elements and only shows other elements
> document.childNodes // shows all children, e.g. DOCTYPE declaration
> document.lastChild.lastChild // navigate to the body element
It is more likely however, that we will find relevant elements based on their
type (e.g. <h2>
), id, CSS class, etc. We can do so with the
querySelector
method that accepts CSS selector string as an argument.
Some example "simple" selectors: element type, #id, .class, ... Note that CSS selectors can be quite sophisticated (with combinations of type, class, etc.), but we are only going to scratch the surface here.
> document.querySelectorAll("h3")
We can also modify these elements via JavaScript:
> document.querySelector("h2").innerHTML = "Class canceled today!"
Note that in this example, we switched from querySelectorAll
to querySelector
. The first returns a list, so we would have to iterate through to find the one we wanted or to change all of them. querySelector
on the other hand, returns the first instance found on the page, which may or may not be unique.
Or in a more complex example, let's add some text to the sidebar menu. We do so by
creating a DOM node and inserting into the tree (with appendChild
or other
related methods like insertBefore
, replaceChild
, etc.).
> const elem = document.querySelector(".sidebar");
> const annc = document.createTextNode("Run away!");
> elem.appendChild(annc);
We can restyle elements using similar tricks:
> document.querySelector(".sidebar").style.background = "red";
JavaScript uses an event listener model for handling interaction. If you want
an element to respond to an event, e.g. a "click" you add an event handler to
the element. The handler is a function you would like to be invoked when the
event happens (i.e. a callback). We can do this generally with addEventListener
,
but there are also older more specific functions for registering event
handlers, e.g. onclick
, onkeydown
, etc. that are widely used (we will use
onclick
extensively with React).
> elem.onclick = (evt => alert("An emergency!"));
with addEventListener
:
> elem.addEventListener("click", evt => alert("Another emergency!"))
Motivating Example: A RGB color picker
We will create a RGB color picker similar in spirit to this example, with three sliders (R, G and B) that control a color swatch.
We will eventually demonstrate many of APIs we discussed today, but let's start
with a simple implementation in which we have statically created all of the
HTML elements, i.e. the swatch and sliders. The role for JavaScript is to
respond to oninput
events for the slider input element by updating the color,
specifically the CSS
background
property, and the color components numeric value.
To do so, we will need to:
- Obtain the DOM elements for the corresponding HTML elements (the swatch
<div>
, slider<input>
s, value<span>
s) withgetElementById
- Create a callback function that updates the swatch background color and the numeric values
- Bind the callback to the slider's
oninput
event. Note that we purposely choose this event instead ofonchange
(the two choices for<input type="range">
. The latter only "fires" when the change is committed by the user, i.e. they release the mouse, not for all changes to an input).
Knowing that we can create HTML elements dynamically, let's DRY up our solution by creating the picker dynamically. Doing so is the focus of today's practical exercise.