30 Days of JavaScript: JavaScript DOM Manipulation, Day 4

30 Days of JavaScript: Building a Simple Weather App
30 Days of JavaScript: JavaScript DOM Manipulation, Day 4

Learn JavaScript DOM manipulation in Day 4! Discover how to access and modify HTML elements, navigate the DOM, and implement event listeners for interactive web pages.

Up to now your JavaScript has run in a vacuum. Today it touches the page. The Document Object Model is the bridge: your HTML, exposed as objects you can read, change, add to, and react to. Get comfortable here and a static page turns into something that responds to the person using it. We’ll cover finding elements, editing them, moving around the tree, and listening for events.

Table of Contents

Accessing and Modifying HTML Elements

Before you can change an element, you have to grab it. There are a handful of ways, and they don’t all return the same thing. getElementById gives you one element or null. getElementsByClassName and getElementsByTagName return a live HTMLCollection that updates as the DOM changes. querySelector takes a CSS selector and returns the first match; querySelectorAll returns a static NodeList of every match, a snapshot that does not update.

Here’s each one in practice:

JS
/**
 * Accessing elements using different DOM methods
 */
let titleElement = document.getElementById('pageTitle');
let navItems = document.getElementsByClassName('navItem');
let paragraphs = document.getElementsByTagName('p');
let mainHeader = document.querySelector('#mainHeader');
let allLinks = document.querySelectorAll('a');

Once you hold an element, you change it by setting properties like innerHTML, textContent, or style:

JS
/**
 * Modifying the content and style of DOM elements
 */
titleElement.innerHTML = 'New Dynamic Page Title';
titleElement.style.color = 'blue';
navItems[0].textContent = 'Home';
navItems[1].style.fontWeight = 'bold';

One honest caveat on innerHTML. It parses whatever you give it as HTML, which makes it an injection sink for cross-site scripting if any of that string came from a user. MDN is blunt about it: a value like <img src='x' onerror='alert(1)'> assigned through innerHTML will run that code. When you’re just writing plain text, reach for textContent instead. It sets text without parsing HTML, so there’s nothing to exploit. Save innerHTML for trusted markup you control, and if you must insert untrusted HTML, sanitize it first.

DOM Traversal and Manipulation

The DOM is a tree, and you can walk it. Properties like parentNode, nextSibling, previousSibling, firstChild, and lastChild move you between nodes. To change the structure itself, use createElement, appendChild, insertBefore, and removeChild.

Worth knowing before it trips you up: the sibling and child properties above count text nodes, and the whitespace between your tags is a text node. So firstChild is often a chunk of whitespace, not the element you expected. When you only care about elements, use nextElementSibling, previousElementSibling, firstElementChild, and lastElementChild instead.

Here’s how you build a new element, give it content, and attach it to a parent:

JS
/**
 * Creates a new paragraph element and appends it to the parent container
 */
let newElement = document.createElement('p');
newElement.innerHTML = 'This is a dynamically added paragraph.';
let parentElement = document.getElementById('contentContainer');
parentElement.appendChild(newElement);

You can drop an element in ahead of another one with insertBefore:

JS
/**
 * Creates and inserts a new heading before a reference element
 */
let newHeading = document.createElement('h2');
newHeading.innerHTML = 'New Section';
let referenceElement = document.getElementById('referenceElement');
parentElement.insertBefore(newHeading, referenceElement);

And take one out by asking its parent to remove it:

JS
/**
 * Removes an element from the DOM by accessing its parent
 */
let elementToRemove = document.getElementById('removeMe');
elementToRemove.parentNode.removeChild(elementToRemove);

Event Handling and Listeners

Events are things that happen in the browser: a click, a keypress, a form submit. You respond by listening for the event and running a function when it fires. addEventListener is how you wire that up.

JS
/**
 * Adds a click event listener to a button element
 */
let buttonElement = document.getElementById('magicButton');
buttonElement.addEventListener('click', function() {
  alert('You have unleashed the magic of JavaScript!');
});

To stop listening, call removeEventListener with the same function reference you added. That last part matters: an anonymous inline function can’t be removed later because you have no handle on it, so name the function if you plan to detach it.

JS
/**
 * Adds and removes a click event listener on a button
 */
function buttonClicked() {
  alert('You have unleashed the magic of JavaScript!');
}
buttonElement.addEventListener('click', buttonClicked);
buttonElement.removeEventListener('click', buttonClicked);

When you have a lot of similar children, don’t put a listener on each one. Put a single listener on the parent and check what got clicked. That’s event delegation, and it keeps working even for elements you add later:

JS
/**
 * Uses event delegation to handle click events on a list
 * @param {Event} event - The event object
 */
let listElement = document.getElementById('itemList');
listElement.addEventListener('click', function(event) {
  if (event.target.tagName === 'LI') {
    alert('Clicked on item: ' + event.target.textContent);
  }
});

Quick note on that check: tagName comes back uppercase for HTML elements, which is why the comparison is against 'LI' and not 'li'. Miss that and the handler silently does nothing.

Conclusion

That’s the core of the DOM. You can find elements with getElementById, querySelector, and friends, change their content and style, and build or remove nodes with createElement, appendChild, and removeChild. You can walk the tree, though remember to prefer the *Element* traversal properties when you want to skip whitespace nodes.

On the events side, you now know how to listen with addEventListener, detach cleanly with removeEventListener, and scale up with delegation instead of wiring a handler to every element. Keep the textContent over innerHTML habit for anything a user typed, and you’ll dodge the most common XSS mistake beginners make.

What's Next?

Day 5 is asynchronous JavaScript and promises. You’ll learn how to fetch data from an API without freezing the page while you wait, which is the foundation for anything that talks to a server. See you there.

Next: 30 Days of JavaScript: Asynchronous JavaScript and Promises, Day 5

Leave a Comment

Your email address will not be published. Required fields are marked *


Scroll to Top