- What is the DOM?
- Why do we need it?
- How do we use it?
- How to select elements from the DOM using selectors
- How to manipulate elements using
.textContent
and.classList
- How to add event listeners
- how to trigger and respond to events
event.preventDefault()
- Recipes for creating/adding/removing HTML elements using javascript
https://github.com/thoughtworks-jumpstart/dom-manipulation
The Document Object Model (DOM) is an in-memory representation of HTML documents. It represents the web page as a javascript object (document
) so that javascript programs can traverse and modify the document structure, style, and content.
Imagine an HTML page with the following structure:
<html>
<head>
<title>Some title</title>
</head>
<body>
<p>First paragraph</p>
<p>Second paragraph</p>
</body>
</html>
The browser exposes a JavaScript interface to query and manipulate this HTML using a global variable called document
. Using plain JS objects and arrays, the DOM can be thought to resemble a structure similar to the following:
document = {
querySelector() { ... },
querySelectorAll() { ... },
createElement() { ... },
children: [
{
tagName: 'HTML',
textContent: '...',
children: [
{
tagName: 'head',
children: [ ... ],
},
{
tagName: 'body',
children: [
{
tagName: 'p',
textContent: 'First paragraph',
},
{
tagName: 'p',
textContent: 'Second paragraph',
},
]
}
]
}
]
};
Note that this is a model of the DOM, but not how the DOM is actually implemented in the browser.
We can also try to visualize the DOM tree of a given HTML document using this tool.
As you can see, there are different types of nodes in a DOM tree:
- Element Nodes
- Text Nodes
In this session, we are going to learn how to manipulate a DOM object using the API it offers.
To start with, let's have a glance with the APIs we need to be familiar with:
Some of the APIs may return multiple nodes as NodeList. It's an array-like object that you can iterate through and get the nodes one-by-one.
This may sound boring now, but basically all interactive webpages that you've visited (e.g. Stripe Connect, pac-man, Fancy Checkout ) are possible because the DOM interface lets us manipulate the contents of a webpage without refreshing a page!
<div id="greeting">Hello World!</div>
let greetingDiv = document.getElementById("greeting");
console.log(greetingDiv);
Note: to refer to the current document object, you can use the global variable document
.
Other element selectors:
// by class name
// (Note: methods which say getElements... (instead of getElement...) return an array)
document.getElementsByClassName("some-class");
// by HTML tag name (this returns an array)
document.getElementsByTagName("h1");
// by css query selector syntax
document.querySelector("li.my-specific-class");
document.querySelector("li#my-specific-id");
// selecting more than 1 element (this returns an array of all html elements meeting the criteria)
document.querySelectorAll("li");
document.querySelectorAll("li.my-specific-class");
You can change the content of the HTML element
greetingDiv.textContent = "Howdy world!";
You can change the style too!
greetingDiv.style.backgroundColor = "gold";
greetingDiv.style.fontFamily = "Helvetica Neue";
greetingDiv.style.fontSize = "30px";
greetingDiv.style.color = "white";
(Warning: .style
is not supported in every browser. This example is for the purpose of illustrating what you can do. A better way to change the style would be via the .classList
attribute)
We can access/update a html element's css class via the .classList
attribute
(See MDN docs for a list of available methods)
.my-new-class {
background-color: gold;
font-family: "Helvetica Neue";
font-size: 30px;
color: white;
}
//list classes
greetingDiv.classList;
//add a class
greetingDiv.classList.add("my-new-class");
//remove a class
greetingDiv.classList.remove("my-new-class");
//check if greetingDiv has a class (returns true or false)
greetingDiv.classList.contains("my-new-class");
The recipe for add a new element to the DOM is as follows:
- create a new element and save it to a variable
- modify any properties of the element
- attach the element to an existing element on the page
Example:
// step 1
let img = document.createElement("img");
// step 2
img.src = "https://media.giphy.com/media/9gISqB3tncMmY/giphy.gif";
img.width = "400";
// step 3
document.body.appendChild(img);
To remove an element from the DOM tree, you can call one of the two APIs:
When users interact with a website (such as moving the mouse around, clicking on a link, etc), some events are generated and can be handled with JavaScripts.
Here is a list of commonly used events:
click
dblclick
mouseover
mouseout
focus
andblur
(try this on a input box or a button)keypress
MDN has a nice list of all DOM events that we can listen for.
Let's look at some examples on handling those events.
We can hook up custom javascript code (e.g. the lines of code that we've just written above to beautify a 'hello world' div), to DOM events (e.g. click, mouseover, mouseout, keydown, keyup, change).
We do this by adding an event listener to a DOM element (by calling .addEventListener('the-event-we-want', callbackFunction)
) and specifying what we want to happen in a callback function
let greetingDiv = document.querySelector("div");
greetingDiv.addEventListener("click", function() {
greetingDiv.textContent = "bye bye";
greetingDiv.style.backgroundColor = "gold";
greetingDiv.style.fontFamily = "Helvetica Neue";
greetingDiv.style.fontSize = "3em";
greetingDiv.style.color = "white";
});
When we add an event listener, the callback function that comes after can take in an event
objet as its first parameter. In the previous example we've left it out and essentially thrown it away because we didn't use it. In some cases, we do want to access the event
object's methods or attributes. Here are some example use cases:
Now we can do the page-styler exercise.
The event object contains several attributes which can be useful. For example:
event.screenX
andevent.screenY
tells us the coordinates of the mouse when the event happened.event.key
- when listening forkeyup
orkeydown
event, we have anevent.key
attribute which tells us which key was pressed (e.g. Enter, Space, 'a', 's', 'd', up, down, left, right, etc).event.target
- is an object which represents the HTML element on which the event was triggered.
Example 1: using event
's properties (e.g. screenX
and screenY
) (Note that event
has been shortened to e
)
let body = document.querySelector("body");
body.addEventListener("mouseover", function(event) {
let rgbColor1 = Math.ceil((event.screenY / 200) * 256);
let rgbColor2 = Math.ceil((event.screenX / 200) * 256);
body.style.backgroundColor =
"rgb(" + [rgbColor1, 100, rgbColor2].join(",") + ")";
console.log(rgbColor1, rgbColor2);
});
// try hovering around your screen!
Example 2: how to (i) listen for keypress, (ii) execute conditional logic using specific keypress event.charCode value, and (iii) get value from input:
<!-- in your html file -->
<body>
...
<input type="text" id='some-input'>
// in your javascript file
let inputDiv = document.getElementById("some-input");
inputDiv.addEventListener("keydown", event => {
console.log(`Key "${event.key}" pressed [event: keydown]`);
console.log(event.target.value);
});
inputDiv.addEventListener("keyup", event => {
console.log(`Key "${event.key}" released [event: keyup]`);
console.log(event.target.value);
});
On the event
object, there are some useful methods as well. You will encounter the following two sometimes.
event.preventDefault()
is a useful method to prevent the default action of the event. For example, when you click on a link, the default action opens a new page. When you submit a form, the default action refreshes the page. That can be irritating, and event.preventDefault()
allows you to stop that so that you can define how you want to handle the event.
Example:
<!-- in your html file -->
<a href="http://www.google.com">google</a>
// in your javascript file
let linkElement = document.querySelector("a");
linkElement.addEventListener("click", function(event) {
event.preventDefault(); // this prevents the default behaviour of clicking a link (i.e. go to link URL)
console.log(event); // let's see what exactly is an event object
});
The usage of event.stopPropagation
is related to a concept called Event Bubbling and Capture. You can also find some discussion on the concept here
HTML and javascript are both loaded sequentially. If your javascript runs to completion before the HTML elements are fully loaded, all of your event listeners and event handlers will not be hooked up. There are 2 ways to make sure this doesn't happen:
- Wrap all of your javascript code as a callback for a
DOMContentLoaded
event
document.addEventListener('DOMContentLoaded', function() {
// place your DOM manipulation code here
}
- Place your javascript code at the bottom of your html body
<html>
<head>
<!-- ... -->
</head>
<body>
<!-- all your html code -->
<script src="./my-javascript-code.js"></script> // place your DOM manipulation javascript code here
</body>
</html>
- https://github.com/thoughtworks-jumpstart/page-styler
- https://github.com/thoughtworks-jumpstart/displayer
- https://github.com/thoughtworks-jumpstart/dice