JS: Intro to DOM Manipulation
Learning Goals
- Access and use the Chrome Developer Tools
- Understand how to query DOM elements
- Understand how to attach event listeners to DOM elements
- Understand how to write functions that can
- Change styles programatically by adding, removing and toggling classes
- Manipulate existing text and html on the DOM
- Add new elements to the DOM programatically
Vocabulary
HTML Element
A building block that makes up the structure of a web page.CSS Selector
A way to identify a set of elements to which a set of CSS styles apply, typically using a tag, class or id.Interface
a shared boundary across which two separate components exchange information.DOM
Document Object Model, the JS interface used to interact with HTMLEvent
Any event which takes place in the DOM, these can be generated by users or the browser.Event Listener
A function invoked on a DOM node which fires theevent handler
when a specified event occurs on the node or its childrenEvent Handler
A function that will run when a specific event occurs
Warm Up
Fork this Animal codepen.
Digest the code in the html file. Ignore the CSS file for now. Read the comments in the JS file outlining what we’re going to try to accomplish. Brainstorm, how might we approach this.
Document Object Model
The Document Object Model, DOM, is an interface for HTML. It is what allows us to use JavaScript to manipulate the elements or content of elements on a page. Think about a form on a website (maybe signing up to use an application, or paying for your food delivery). Somehow, JavaScript has to know what was typed into an input field, know that the submission button was clicked, and be able to do whatever needs to happen next (maybe showing you a confirmation message). If we didn’t have the DOM, this wouldn’t be possible.
Once the browser runs/interprets our HTML, it creates the DOM. The DOM represents what is in the HTML, but it is formatted in a way that we can interact with those elements via JavaScript. It is not the exact same thing as your HTML document.
Here’s a small HTML file:
Here’s the DOM tree that would be created:
Image credit: bitsofcode.
This is an abstract concept and can be hard to wrap your mind around - we’ll come back to it. For now, you need to know two things:
- The DOM is essential to building functionality on a page
- We can use the
document
variable in our JavaScript file to interact with the DOM. From there, we can travel through the DOM and ask for specific elements, or instruct the program to change elements.
Check It Out
In your browser dev tools, type in document
. Click that small arrow to the left of the output.
- What do you see?
- Where else can you find that content?
At any time, you can call document
to access the DOM. This is very close to the same thing as viewing the document in the Elements panel of the Dev Tools. We won’t get into the details of those small differences; if you are interested, start by reading this blog post.
Accessing a Specific Element
Our document
object - it really is a object! - comes built-in with a lot of methods we can use. Today, we will learn about .querySelector()
. This method allows us to request a specific element from the DOM tree, by its selector. It uses the same syntax for selectors that CSS does.
Remember that in JS, unlike Ruby, we can use dot notation to access methods or properties of an object. In JS, we always need ()
to invoke a method or function.
Here’s an example:
var title = document.querySelector('h1');
title;
//=> <h1>JS: Intro to DOM Manipulation</h1>
Explore
Open the Dev Tools while you are looking at this lesson. In the console, paste this in:
var box = document.querySelector('.call-to-action');
Then, call the box
variable (by typing in box
, then hitting return
).
Consider:
- What is returned?
- Where does that element appear in the browser?
- There are several sections with that class name on this page - why do you think that only one of them is stored in the
box
variable? - If we were wanting to get this Explore box instead, is there a unique identifier on it that we could use to be more specific when querying?
- Let’s change our code to use that unique identifier for this box instead!
We’ve already seen three of the different selector options we can use in our query:
- by html element tag:
document.querySelector('h1')
- by class:
document.querySelector('.call-to-action')
- by id: :
document.querySelector('#explore-querySelector')
Take Note:
- selectors must always be in quotation marks (single or double)
- class selectors must be prefaced with a
.
- id selectors must be prefaced with a
#
Your Turn
In the warmup Animal codepen, query the three elements we’re going to want access to. Save each in a well-named variable.
- the title
- the elephant fact
- the image
Then, add a console.log() to your code for each variable so you can see what is stored in it. You can see this in the dev tools console (command + option + i) or in the codepen console.
Include a helpful string in each console log to serve as a ‘label’ for what you’re printing. Separate the string from the variable with a comma.
console.log('title', title)
Updating Content of an Element
Now that we know how to query elements, let’s look at how to update the content of an element we’ve accessed.
var title = document.querySelector('h1');
When we query a DOM element and store it in a variable, we now have access to all the properties and methods that are attached to that DOM element. We won’t learn about all of them today. Just a few to get you started.
innerText
The innerText property holds the text that lives inside of a given element. In our codepen, the text content inside of the h1
is Elephants!
. We can access that text content by accessing the element’s innerText
property. We can use the =
assignment operator to change it.
Try It Together
In our codepen let’s:
- Change our console.log()s so that they print each element’s innerText
- What happens when we try to print the
.innerText
of our<img>
element?
- What happens when we try to print the
- Write a line of code that changes the title’s text to a different animal
- Write a line of code that changes the elephant fact to a different animal fact
Consider:
How would we change the elephant image to a different image? Hmm…
We’ve seen from the console log that we won’t be able to use innerText
property for the image.
Looking at the <img>
in the html, is there a different property we might be able to manipulate?
Psst…here is a short link for a hedgehog image if you want to use it. https://upload.wikimedia.org/wikipedia/commons/thumb/7/72/Igel.JPG/440px-Igel.JPG
Your Turn
Fork this Report Card CodePen. In the JS
file of the CodePen, complete the steps listed below. Work through each bullet one at a time.
- Declare a variable
studentName
and assign the value of the element with the class ofstudent
to it - Declare a variable
bestGrade
and assign the value of the element with a class ofgrade-2
to it
Now that we’ve queried the elements we need, we’re going to change the data being displayed to users. So we need somewhere to store the new data that we’re going to display instead.
- Declare a variable
newData
to store an object with keys ofnewName
andnewGrade
. Make the value of each key be the new student name and grade that you’ll want to change the display to.
Now, change the text of each queried element to display the data you saved in your newData object.
- Change the
innerText
of thestudentName
variable to be thenewName
value from yournewData
object (use dot notation) - Change the
innerText
of thebestGrade
variable to be thenewGrade
value from yournewData
object
Event Listeners
Changing stuff on the page with JavaScript is great, but you might as well have just written it in the markup to begin with. The real power of JavaScript comes into play when we write code that responds to user actions.
This power emerges when we start listening for user events. This is the crux of front-end engineering. We present a user interface and then as the user interacts with the UI, we change and update what they see.
Try It Together
Let’s add an eventListener to our Report Card codepen. We still want to change the student name and grade, but we want to do so only when the user takes an action. Let’s say we want to change that data if a user clicks on the “Report Card” title
- We’ll use
document.querySelector()
to grab the “Report Card”<h1>
and save it to a variable - Now we can add an eventListener to that
<h1>
title.- We want the eventListener to listen for a “click” event that happens on that title element.
- When the “click” happens on the title element, we want to invoke a function that does all the things we want to do. Let’s call that function
editReportCard
.
- You’ve already written the code to change the student name and grade. Now we can move this code into the
editReportCard
function that is being invoked by the eventListener.
Consider:
- Why don’t we use
()
to invoke the function within the eventListener? What happens if we do?
Possible solution
const studentName = document.querySelector(".student")
const bestGrade = document.querySelector(".grade-2")
const title = document.querySelector("h1")
const newData = {
studentName: "Heather",
bestGrade: "A+"
}
title.addEventListener("click", editReportCard)
function editReportCard() {
studentName.innerText = newData.studentName;
bestGrade.innerText = newData.bestGrade;
}
Pretty cool how one simple line of code lets us respond to user actions, right?
title.addEventListener("click", editReportCard)
Your Turn
In the codepen from the Warm Up, follow the bullets below to add in an Event Listener. We still want to change the title, fact, and image, but we want to do so only when the user clicks a button.
- Add a “Change Animal” button to your html above the <h1> element. Give it an id of ‘change-animal-btn’.
- In your js file, use
document.querySelector()
to grab that button by its id and save it to a variable - Now add an eventListener to the button.
- We want the eventListener to listen for a “click” event that happens on that button.
- When the “click” happens on the button, invoke a function that does all the things we want to do. Call that function
changePageContent
.
- You’ve already written the code to change the title, fact and image. Move this code into the
changePageContent
function that is being invoked by the eventListener.
Psst…here is a short link for a hedgehog image if you want to use it. https://upload.wikimedia.org/wikipedia/commons/thumb/7/72/Igel.JPG/440px-Igel.JPG
Explore
There are many types of events beyond just “click”. Explore this list of Mouse Events on MDN. Scroll beyond just mouse events to explore keyboard events, focus events, etc.
Think of a webpage you often visit. Maybe it’s a social media page, maybe it’s your bank’s website, maybe it’s a news site. Take a moment and consider all the event listeners that might be on that page! What type of events might they be listening for? What functionality occurs when those events happen?
More Programmatic DOM Manipulation
innerHTML
The innerHTML property essentially works the same, but has the capacity to do something a little more complex for us.
It can not only insert plain text, but it can insert HTML elements. For the example below, we will query and manipulate the Try It Together section above.
Check It Out
In your browser dev tools, copy paste the following code:
var box = document.querySelector('#try-it');
Then type box
and hit return to see the value of that variable. Click on the dropdown arrow to see the full html within the box section.
What do you think the following code will do? Make a guess. Now, run it in your console:
box.innerHTML = `
<h3>Pizza is Tasty</h3>
<p>Yum!</p>
`;
Log the box variable again and open up the dropdown to see what has changed.
What do you think the following code will do? How will it be different than the code we just ran? Guess, then run it in your console:
box.innerHTML += `
<p>Pepperoni!</p>
`;
Log the box variable again and open up the dropdown to see what has changed.
Key Takeaways
We can access the text/html content of an element using dot notation with innerText
/innerHTML
.
We can change the text/html content of an element using =
or +=
.
Changing Styles Programmatically
It isn’t uncommon to see an application change the color of something after a user interacts with it.
We see things like…
- Toggling light and dark mode
- A grey check mark turn into a green one
- A box or button changing colors once we’ve already clicked it, or visited that link … on almost a daily basis!
We talked earlier about how powerful the DOM is because it allows us to interact with HTML elements via JavaScript. This also gives us the power to modify the styles applied to said elements via JavaScript.
Even though you haven’t yet learned CSS, we will briefly discuss two ways to do this:
- Directly manipulate the style of the element through inline styles (not best practice)
- Add or remove classes from the element (the preferred way).
Directly Manipulate the Style of the Element
We can use dot notation to call the .style
property on an element, then reference any CSS property and assign a new style value.
Change My Border!
We’ll use inline styling to change the border of this box to purple.
Type or copy/paste the code snippet below into the dev tools to see it change!
var box = document.querySelector('#change-my-border-box');
box.style.borderColor = "purple";
We actually avoid inline styling because we usually want to make more than one tiny change and each inline style change requires it’s own line of code. This can make for long functions that are difficult to read. Adding or removing classes (examples below) is strongly preferred. Plus, that lets us keep all our styling declarations in one place - the CSS file. Much more maintainable!
Then Why Bring It Up?
Often times, your instructors at Turing will share with you an overview of something, including the multiple ways something can be done, but tell you that one is preferred. Why do we bother to bring up the less-than-ideal approaches?
As a developer, a huge part of your job is researching and learning on your own. That will also be a huge part of being a student at Turing. You’ll sift through documentation, blog posts, and Stack Overflow posts. It’s important to be familiar with the multiple ways of doing things, different syntax, best practices, etc. so you can more easily sift through those resources and determine what is good and helpful.
Add or Remove Classes from the Element
Since it’s common to need to change multiple CSS declarations, we can instead add or remove a class from the list of classes a given element has on it.
Check It Out
Fork this Color Box CodePen and follow the directions below:
- Read through the code that is currently written, and observe the result in the mini-browser
- Un-comment line 3. What changed about the box? What remained the same?
-
Un-comment line 4. What changed about the box? What remained the same?
- Read up on this documentation to learn how to remove a class from an elements classList! Hint:
toggle
may soon be a friend of yours, as well!
In summary, in your CSS file you can **apply styles to a specific class selector - like a “pink” class. Then, in your JS file, you can use button.classList.add('pink')
to apply all those styles at once by adding that “pink” class to the specified button. **
Note that if you are going to add/remove a class via JavaScript, that class should be declared below the original styling for the given element in the CSS file. This is because of the cascading nature of CSS; any time rules conflict, the rule declared lower on the sheet will take precedence.
Key Takeaways
- In your CSS file you can apply styles to specific class selectors. Then you can use JS to add/remove/toggle that class to specified elements so that it will/will not have those styles applied.
- If you’re going to add/remove a class with JavaScript, the styling for that class should be declared below the original styling for the given element in the CSS file.
- We can apply styles to an element directly with JavaScript by manipulating the
.style
property - but we do not want to do this.
Putting it all together
We’ve seen that DOM manipulation based on user interaction requires at least 3 steps.
- Elements: Querying the elements we’ll need
- Event Listener: Adding the event listener
- Function: Creating the function that the event listener will trigger
As you work through the practice below, first psuedocode out what each of those three steps entail so you can take a methodical approach to writing the code.
Your Turn
In the Animals codepen from the warmup:
Add Fact:
- Add another button with “Add Fact” text and id of “add-fact-btn”.
- Write code so that when a user clicks the Add Fact button, another
<p>
element with another animal fact is added to the<section>
element, just below the image
Change Color:
- Add another button with “Change Color” text and id of “change-color-btn”
- Write code so that when a user clicks the Change Color button, the “.blue” class styling from the CSS file is applied to the
<section>
element, making its background blue.
Spicy:
- Refactor your Add Fact code so that it uses the .insertAdjacentHTML() method to acheive the same result
- Refactor your Change Color code so that it toggles the background color blue/not-blue with each click.
Wrap Up
Let’s take a few minutes to look back at what we’ve started to learn!
- What is the DOM? How is it created?
- How can we grab elements from the DOM to work with them in our JS?
- How can we change the text of an element?
- How can we change the html of an element?
- How can we change styling applied to an element?
- What are the 3 steps for writing code that response to user interactions?