DOM and Events

Objectives

  • Define what DOM stands for and what it refers to

  • Select elements from the DOM using selectors

  • Add events to elements in the DOM

What is the DOM?

When the broswer loads a webpage, it takes in all the information about what needs to be displayed on the page, and creates a bunch of Javascript objects that represent the elements on the page. These objects mirror the html hierarchy, where the highest order parent is the document object.

Document Object Model: A model of all the html elements on a web page - usually represented by a tree of nodes - that is created by the browser when a web page is loaded.

DOM on MDN

Exercise

Visit this website and open up the dev tools. Look at the Elements tab. Try expanding the collapsed elements to explore the structure of the document more. Now go to the Console tab and run document to see the document object. Notice that this is the same hierarchy from the Elements tab. The Elements tab of the console shows us a vertical representation of the DOM

Now draw out a diagram (tree of nodes) that represents the DOM that is loaded by this webpage.

DOM manipulation

What is DOM manipulation?

Using Javascript, we can read and/or change the DOM, i.e. make the webpage do something interesting. That's the fun part of JS!

Examples:

  • Change or Remove HTML elements

  • Create and Add HTML elements

  • Change the CSS styles of HTML elements

  • Read & Change attributes of HTML elements (href, src, alt, etc)

  • Attach event listeners to HTML elements (click, keypress, submit, etc.)

Why would we want to manipulate the DOM?

Without DOM manipulation, web pages would just be static visuals with no changes and no interaction with the user.

Example:

  • Clicking on buttons would do nothing

  • You could never type into an input field

  • You could never zoom into an image or stop/start a video

How do we manipulate the dom?:

The document object represents the DOM. It has several methods available to help us grab specific elements!

Selecting DOM Elements by HTML

  • document.getElementById

  • document.getElementsByClassName

  • document.getElementsByTagName

html

<div id="hello" class="square">
  Hello, world!
</div>

<div id="gb" class="square">
  Goodbye, world!
</div>

css

#hello {
  background: yellow;
}

#gb {
  background: orange;
}

.square {
  height: 100px;
  width: 100px;
}

js

var myDiv = document.getElementById('hello');

console.log(myDiv);

var theSquares = document.getElementsByClassName("square");

console.log(theSquares[0]);
console.log(theSquares[1]);

var theDivs = document.getElementsByTagName("div")

console.log(theDivs[0]);
console.log(theDivs[1]);

Try using the id to console.log the first div. What document method did you use?

Selecting DOM Elements by CSS

querySelector methods allow us to use CSS Selector syntax to grab DOM objects.

  • document.querySelector

  • document.querySelectorAll

js

var myDiv2 = document.querySelector('#gb');

console.log(myDiv2);

var mySquares2 = document.querySelectorAll('.square')
;

console.log(mySquares2[0])
console.log(mySquares2[1])

var myDivs2 = document.querySelectorAll('div');

console.log(myDivs2[0])
console.log(myDivs2[1])

Changing DOM Elements

changing styles

myDiv.style.backgroundColor= 'chartreuse';
myDiv2.style.height="300px";

Changing Content

single DOM element changes

myDiv.innerText = "I love WDI"
myDiv2.innerHTML = "<h2>I love GA</h2>"

Manipulating Multiple DOM Elements

What if I want to do something to both divs at once?

// this wont work!
theSquares.style.border = "2px dashed black"

// but this will
for(var i = 0; i<theSquares.length; i++) {
  theSquares[i].style.border ="dashed 2px black"
}

Be Careful! Multi-element selectors like querySelectorAll, getElementsByTagName, and getElementsByClassName don't actually return an array; they return something called an HTML collection. This means that many array methods (iterators, in particular, which we'll learn about later) wont work. If you run into this problem, you can use the Array.from method to convert it to an array.

Accessing and changing element attributes

What if I want to change an attribute, like the src on this img?

html

<img src="http://www.placekitten.com/200/200">

There are 2 ways to get and set attributes of a DOM element. You can access the properties directly or use use get/setAttribute methods. It's important that you know both exist, but generally accessing the properties directly is more consistent across browsers.

var photo = document.querySelector("img");

//get using property
console.log(photo.src);

//set using property
photo.src = "https://picsum.photos/200/200";

//get using getAttribute method
photo.getAttribute("src");

//set using setAttribute method
photo.setAttribute("src","https://placebear.com/200/200");

Classes

Acessing, getting, setting CSS classes is slightly different than other properties.

First you can directly access the class attribute by using the className property of a DOM element.

console.log(document.querySelector('div').className);

This works fine, but since elements can have multiple classes (separated by spaces) this often leads to needing to do some string parsing, so intead, we often use the classList attribute, which gives us "a DOM token list".

console.log(document.querySelector('div').classList);

And just like the HTML collection, we can access the values in the classList like an array.

var helloDiv = document.querySelector('div');
console.log(helloDiv.classList[0]);

You can add to the classList:

helloDiv.classList.add('yellow');
console.log(helloDiv.classList);

You can also check if an item has a class (returns true or false)

console.log(helloDiv.classList.contains('square'))

You can remove a class from the classList:

helloDiv.classList.remove('yellow');
console.log(helloDiv.classList.contains('yellow'))

DOM Events

DOM events are the bedrock of interactivity on web pages. They enable us as developers to implement event-driven programming. This programming paradigm is such that much of our code, written as event listeners, runs in response to events being triggered during run-time.

Lots of events are generated within the browser, for example, when:

  • a user moves or clicks the mouse

  • a user presses a key

  • when a form is submitted

  • when the page has finished loading or has been resized

  • etc.

Take a gander here at the type and sheer number of events.

DOMContentLoaded Event

All of the selectors we've been using rely on the use of DOM elements. However, if the JavaScript loads before all the DOM elements load, the selectors won't recognize that some of them exist! To avoid this problem, there's an event called DOMContentLoaded that we can encapsulate our code inside. Then, we can guarantee that the DOM elements exist before manipulating them.

document.addEventListener('DOMContentLoaded', function() {
  //code and events go here
});

Event Listeners

An event listener is a function, more specifically, a callback function, that is called when an event fires. You may also hear them referred to as event handlers (depending upon how they are "registered" with the browser).

There are three different approaches for attaching event listeners to elements:

  1. In the HTML (inline) - <button id="reset-btn" onclick="reset()"> - this isn't great because it embeds JavaScript code into our HTML, violating our separation of concerns.

  2. Assigning to DOM elements' properties - resetBtn.onclick = reset; - this is a better choice but it is limited to adding single event listeners.

  3. Calling addEventListener on a DOM element - this is the preferred way to do it since it is functional and supports adding multiple listeners at a time.

Event Listener Syntax

addEventListener([event type],[function that you want to run when the event fires])

document.addEventListener('DOMContentLoaded', function(){

    var helloDiv = document.getElementById("hello")

    var alertClick = function() {
        console.log("HELLO WAS CLICKED")
    }

    helloDiv.addEventListener("click", alertClick)

}

Removing Event Listeners

It's possible to remove an added event listener, however, only if a named function was used as the callback:

document.addEventListener('DOMContentLoaded', function(){

    var helloDiv = document.getElementById("hello")

    var goodbyDiv = document.getElementById("gb")

    var alertClick = function() {
        console.log("HELLO WAS CLICKED")
    }

    helloDiv.addEventListener("click", alertClick)

    goodbyDiv.addEventListener("click", function() {
        console.log("removing event listener!")
        helloDiv.removeEventListener("click", alertClick)
    })

})

Exercise:

Research a different event listener (not click) and apply it to one of the divs. Share your code in slack when you're done!

The Callback and the Event Object

The first parameter to addEventListener is the name of the event that we are listening for (e.g. click, change, keydown, etc.). The second parameter is the callback function that we pass in to tell the browser what to do when this event occurs. This callback is allowed to use a very special parameter: the event object (shown above as the parameter event but is frequently abbreviated to evt or e).

This event object is passed into our event listener callback by the JavaScript engine in the browser. It contains may useful details about the event. Of special note are:

  • event.target - this contains a reference to the actual DOM element that generated the event. In the case of a 'click' event, event.target would be the element that was clicked on.

  • Several ...X and ...Y properties that tell where the click occurred.

  • event.preventDefault() - a function to immediately disable the default action of this event for the given element. Useful for preventing unwanted form submissions or link navigation.

  • event.stopPropagation() - a function for disabling the "bubbling" of this event up the DOM. More on this below...

Note: JavaScript's this keyword within the listener function will also be set to the DOM element that addEventListener was called on, so you can use it instead of event.target if you like.

Event Bubbling

When an event occurs on an element, that event, whether it is listened to on that element or not, bubbles up through the DOM, all the way up to the document object.

All event listeners registered for the same event, such as click, will be invoked along the path to the document element - unless one of those listeners calls the event object's stopPropagation() method.

This passing of the event up the DOM tree allows for a very nice feature called Event Delegation.

Event Delegation

Imagine an unordered list with many list items inside it. Each list item in our app needs to have a click event that allows it to perform some unique action. We could probably add an event listener to each list item and if there weren't too many, it woudn't be too bad. But imagine that this unordered list can grow and add hundreds of additional list items programmatically while the app is running. We can't add them to each element manually. Instead, because of the bubbling of events, we can delegate the parent of the list items to handle the event.

Event delegation allows us to register a single event listener that can respond to events triggered by any of its descendants. Much more efficient!

All we would need to do is view the event.target property of the event object to see what element was referenced there. This would be the child element that generated the event.

References

Last updated