DEV Community

Cover image for The Complete Guide to Becoming a Web Developer: Part 3
Ahmed Radwan
Ahmed Radwan

Posted on • Originally published at nerdleveltech.com

The Complete Guide to Becoming a Web Developer: Part 3

Hello there, web development enthusiasts! If you've ever built a website, you've interacted with the Document Object Model (DOM). But what exactly is the DOM, and why is it so crucial in web development? Buckle up, because we're about to dive deep into the fascinating world of the DOM!

If you're new here you can start with part 1 and part 2.

Introduction

The DOM is like the backbone of your web page. It's a programming interface that allows your web page to come to life, interact with users, and change dynamically. When you load a web page, the browser creates the DOM of that page, which is essentially an object-oriented representation of the web page's structure.

Think of the DOM as a tree-like structure where each branch ends in a node, and each node contains objects. These objects are parts of your webpage like HTML tags, text content, links, images, etc. The DOM allows JavaScript to access and manipulate these objects, change their properties, react to events, and even create new elements and attributes.

Understanding the DOM is fundamental to web development because it's the bridge between your HTML/CSS and JavaScript. Without the DOM, your JavaScript code wouldn't be able to interact with your web page's content and structure.

Section 1: Understanding the DOM

Welcome to the first section of our deep dive into the DOM! Now that we've covered the basics, let's roll up our sleeves and get into the nitty-gritty of what the DOM is, how it works, and its role in JavaScript.

What is the DOM?

The Document Object Model, or DOM, is a programming interface for HTML and XML documents. It represents the structure of a document in a way that's easy for programming languages like JavaScript to interact with. The DOM transforms your document into an object-oriented hierarchy or a "tree" of nodes. These nodes are objects that represent parts of the document, such as elements, attributes, and text.

Imagine a family tree. You have the grandparents at the top (the root node), their children below them (child nodes), and their grandchildren below them (leaf nodes). Similarly, in the DOM tree, the document object is the root node, elements like <body> and <head> are child nodes, and the elements and text inside them are leaf nodes.

How Does the DOM Work?

When you load a web page, the browser fetches the HTML and parses it, creating the DOM. This DOM is stored in the browser's memory, not as a string, but as a set of interconnected objects with properties and methods.

Let's take a look at a simple HTML document:

<!DOCTYPE html>
<html>
<head>
    <title>My Web Page</title>
</head>
<body>
    <h1>Welcome to my web page!</h1>
    <p>This is a paragraph.</p>
</body>
</html>

On the other hand, the browser would create a DOM that looks something like this:

document
|-- html
    |-- head
        |-- title
            |-- "My Web Page"
    |-- body
        |-- h1
            |-- "Welcome to my web page!"
        |-- p
            |-- "This is a paragraph."

This tree-like structure is what JavaScript interacts with. It can access any node, change its contents, apply styles, add or remove nodes, and so on. For instance, if you want to change the text inside the <h1> tag or you can select the <body> node and add a new child node to it. You do many things and you use JavaScript to select these nodes in the DOM and manipulate its contents.

The Role of the DOM in JavaScript

The DOM is the meeting point between HTML and JavaScript. It's what allows JavaScript to interact with the HTML, changing the document's structure, content, and styling on the fly.

Without the DOM, your JavaScript code would have no way of accessing or manipulating the HTML. You wouldn't be able to respond to user events, update the content dynamically, or create interactive web experiences.

In other words, the DOM is what turns your static HTML document into a dynamic, interactive web page. It's the magic behind every JavaScript-powered website or web app you've ever used.

Section 2: Selecting Elements

Alright, now that we've got a solid understanding of what the DOM is, let's get our hands dirty and start interacting with it. The first step in DOM manipulation is selecting the elements you want to work with, and that's where querySelector and querySelectorAll come into play.

querySelector

querySelector is a powerful method that allows you to select the first element that matches a specified CSS selector(s) in the document. It's like a Swiss Army knife for DOM selection. You can use it to select elements by their tag name, class, ID, attribute, and more.

Here's how it works:

let element = document.querySelector(selector);

The selector is a string containing one or more CSS selectors separated by commas. querySelector returns the first element that matches any of the specified selectors. If no matches are found, it returns null.

Let's say we have the following HTML:

<div class="container">
    <h1 id="title">Hello, World!</h1>
    <p class="text">Welcome to my web page.</p>
</div>

Here's how you can use querySelector to select these above elements:

let title = document.querySelector('#title'); // selects the element with the ID "title"
let text = document.querySelector('.text'); // selects the element with the class "text"
let div = document.querySelector('div'); // selects the first <div> element

querySelectorAll

While querySelector returns the first matching element, querySelectorAll returns a NodeList (think of it as a collection) of all elements in the document that match the specified CSS selector(s).

Here's how you can use it:

let elements = document.querySelectorAll(selector);

Again, selector is a string containing one or more CSS selectors separated by commas. querySelectorAll returns a NodeList of all elements that match any of the specified selectors. If no matches are found, it returns an empty NodeList.

Using the same HTML as before, here's how you can use querySelectorAll:

let divs = document.querySelectorAll('div'); // selects all <div> elements
let elements = document.querySelectorAll('.text, #title'); // selects all elements with the class "text" and the ID "title"

Remember, querySelectorAll returns a NodeList, not an array. While you can use some array methods on a NodeList (like forEach), others (like map and filter) are not available. If you need to use these methods, you can convert the NodeList to an array using Array.from().

And there you have it! You now know how to select elements in the DOM using querySelector and querySelectorAll. These methods are incredibly powerful and versatile, and they're the foundation of all DOM manipulation. So go ahead, start selecting elements and playing around with them.

Section 3: DOM Most Important Properties & Methods

Welcome to the heart of DOM manipulation: properties and methods. Now that we know how to select elements, it's time to learn how to interact with them. In this section, we'll explore a variety of properties and methods that allow us to read, change, add, and remove elements and their content. Let's dive in!

classList

The classList property returns a live DOMTokenList collection of the class attributes of the element. This property is useful for adding, removing and toggling CSS classes on an element.

let element = document.querySelector('#myElement');
element.classList.add('new-class'); // adds 'new-class' to the element
element.classList.remove('new-class'); // removes 'new-class' from the element
element.classList.toggle('new-class'); // toggles 'new-class'

getAttribute() and setAttribute()

getAttribute() returns the value of a specified attribute on the element. setAttribute() sets the value of an attribute on the specified element.

let link = document.querySelector('a');
let href = link.getAttribute('href'); // gets the href attribute
link.setAttribute('href', 'https://www.example.com'); // sets the href attribute

appendChild(), append(), and prepend()

appendChild() adds a node to the end of the list of children of a specified parent node. append() and prepend() do the same but allow you to add multiple nodes, and they also accept strings to be inserted as text nodes.

let newElement = document.createElement('div');
document.body.appendChild(newElement); // appends newElement to the body

let textNode = document.createTextNode('Hello, World!');
newElement.append(textNode, ' Welcome!'); // appends two nodes to newElement

newElement.prepend('Greetings! '); // prepends a text node to newElement

removeChild() and remove()

removeChild() removes a child node from the DOM and returns the removed node. remove() removes the current node from the DOM.

let element = document.querySelector('#myElement');
document.body.removeChild(element); // removes element from the body

element.remove(); // removes element

createElement, innerText, textContent, and innerHTML

createElement() creates a new element node. innerText and textContent get or set the text content of a node and its descendants. innerHTML gets or sets the HTML content (inner HTML) of an element.

let newElement = document.createElement('div'); // creates a new <div> element

newElement.innerText = 'Hello, World!'; // sets the text content
console.log(newElement.textContent); // gets the text content

newElement.innerHTML = '<strong>Hello, World!</strong>'; // sets the HTML content

value, parentElement, children, nextSibling, previousSibling, and style

value gets or sets the value of the value attribute of a text field. parentElement returns the parent element of the specified element. children returns a live HTMLCollection of child elements. nextSibling and previousSibling return the next and previous sibling of the node in the tree, or null if there are no siblings. style gets or sets the inline style of an element.

let input = document.querySelector('input');
input.value = 'Hello, World!'; // sets the value

let parent = element.parentElement; // gets the parent element
let children = element.children; // gets the child elements

let next = element.nextSibling; // gets the next sibling
let previous = element.previousSibling

Section 4: Event Handling and Most Common Events in JavaScript

Welcome to the exciting world of event handling in JavaScript! This is where your web pages truly come to life, responding to user interactions in real time. From simple clicks to complex drags and drops, JavaScript events are the heart of interactive web experiences. Let's dive in!

What is Event Handling?

In JavaScript, an event is a signal that something has happened. This could be a user action, like clicking a button or submitting a form, or a browser action, like loading a page or resizing a window.

Event handling is the process of setting up listeners on elements that respond to these events. When an event occurs, the listener executes a JavaScript function, known as an event handler.

Event handling is crucial in JavaScript because it allows your web pages to respond to user interactions and create dynamic, interactive experiences.

Common JavaScript Events

Let's take a look at some common JavaScript events and how to handle them.

Clicks

The click event fires when a user clicks an element.

let button = document.querySelector('button');
button.addEventListener('click', function() {
    console.log('Button clicked!');
});

Drags and Drops

The dragstart, dragover, and drop events are used to create drag and drop functionality.

let draggable = document.querySelector('.draggable');
let dropzone = document.querySelector('.dropzone');

draggable.addEventListener('dragstart', function(event) {
    event.dataTransfer.setData('text/plain', draggable.id);
});

dropzone.addEventListener('dragover', function(event) {
    event.preventDefault(); // prevent default to allow drop
});

dropzone.addEventListener('drop', function(event) {
    event.preventDefault(); // prevent default action (open as link for some elements)
    let id = event.dataTransfer.getData('text');
    let draggable = document.getElementById(id);
    dropzone.appendChild(draggable);
});

Hovers

The mouseover and mouseout events fire when the mouse pointer enters and leaves an element, respectively.

let element = document.querySelector('#myElement');
element.addEventListener('mouseover', function() {
    element.style.backgroundColor = 'yellow';
});
element.addEventListener('mouseout', function() {
    element.style.backgroundColor = '';
});

Scrolls

The scroll event fires when an element's scrollbar is being scrolled.

window.addEventListener('scroll', function() {
    console.log('Scrolling!');
});

Form Submission

The submit event fires when a form is submitted.

let form = document.querySelector('form');
form.addEventListener('submit', function(event) {
    event.preventDefault(); // prevent form from submitting normally
    console.log('Form submitted!');
});

Key Presses

The keydown, keypress, and keyup events fire when a user is pressing or releasing a key.

window.addEventListener('keydown', function(event) {
    console.log('Key pressed: ' + event.key);
});

Focus/Blur

The focus and blur events fire when an element gets or loses focus.

let input = document.querySelector('input');
input.addEventListener('focus', function() {
    console.log('Input focused!');
});
input.addEventListener('blur', function() {
    console.log('Input blurred!');
});

Double Click

The dblclick event fires when an element is double-clicked.

let button = document.querySelector('button');
button.addEventListener('dblclick',

Section 5: Asynchronous JavaScript and the DOM

As we near the end of our deep dive into the DOM, let's take a moment to peek into the future. Specifically, let's talk about asynchronous JavaScript and how it interacts with the DOM. This is a vast topic that deserves its own series of articles, but we'll touch on it briefly here to give you a taste of what's to come.

In JavaScript, most operations are synchronous, meaning they block further execution until they're finished. However, certain operations, like fetching data from a server or setting a timer, are asynchronous. They're initiated now, but they finish later, allowing the rest of your code to continue running in the meantime.

Asynchronous JavaScript is crucial for creating smooth, responsive web experiences. It allows your web page to stay interactive even when it's performing time-consuming tasks, like loading data or images.

Here's a simple example of asynchronous JavaScript in action:

console.log('Fetching data...');

fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => {
        console.log('Data fetched!');
        // Now you can use the data to update the DOM
    });

console.log('This will run before the data is fetched!');

In this example, the fetch function returns a Promise, which is a placeholder for the future result of an operation. The then method is used to schedule callbacks to be run when the Promise is fulfilled, or in other words, when the data is fetched.

This is just the tip of the iceberg when it comes to asynchronous JavaScript. There's so much more to learn, including callbacks, Promises, async/await, and more. But don't worry, we'll cover all of that and more in future articles. For now, just know that asynchronous JavaScript is a powerful tool for creating dynamic, interactive web experiences, and it plays a crucial role in DOM manipulation.

Stay tuned for more, and keep practicing what you've learned so far. You're well on your way to becoming a DOM manipulation master!

Section 6: Common Pitfalls in DOM Manipulation

Alright, we've covered a lot of ground so far. We've learned what the DOM is, how to select elements, and how to use properties and methods to manipulate those elements. Now, it's time to talk about some common pitfalls in DOM manipulation. Even experienced developers can fall into these traps, so let's shine a light on them and learn how to avoid them.

Pitfall 1: Not Waiting for the DOM to Load

One of the most common mistakes is trying to manipulate the DOM before it's fully loaded. If your JavaScript runs before the HTML is parsed, it won't be able to find the elements it's supposed to interact with.

To avoid this, you can use the DOMContentLoaded event, which fires when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.

document.addEventListener('DOMContentLoaded', function() {
    // Your code here
});

Pitfall 2: Overusing innerHTML

While innerHTML is a powerful property that allows you to get or set the HTML content of an element, it can be a source of performance issues and security vulnerabilities.

Every time you use innerHTML, the browser has to parse the new HTML string into a DOM tree, which can be slow for large amounts of HTML. It also exposes you to cross-site scripting (XSS) attacks if you're not careful with user input.

As a best practice, use textContent or innerText when dealing with text, and createElement, appendChild, and other DOM methods when creating new elements.

Pitfall 3: Forgetting That NodeList Is Not an Array

When you use querySelectorAll, it returns a NodeList, which is an array-like object but not an actual array. This can lead to confusion when you try to use array methods like map, filter, or reduce on a NodeList and get a TypeError.

To avoid this, you can convert a NodeList to an array using Array.from() or the spread operator (...).

let elements = document.querySelectorAll('div');
let elementsArray = Array.from(elements); // or [...elements]

Pitfall 4: Not Properly Handling Event Propagation

JavaScript events propagate, meaning they start from the deepest element (the target) and then bubble up to the root of the tree. If you're not careful, an event handler on a parent element might fire when you interact with a child element.

To prevent this, you can use event.stopPropagation() in your event handler. However, use it sparingly, as it can make your code harder to debug and understand.

element.addEventListener('click', function(event) {
    event.stopPropagation();
    // Your code here
});

Remember, everyone makes mistakes, and that's how we learn. The key is to understand these common pitfalls and learn how to avoid them. Happy coding!

Section 7: Building a Simple To-Do List Project

Alright, it's time to put all that theory into practice. We're going to build a simple project that demonstrates all the concepts we've covered in this article. We'll create a dynamic, interactive to-do list where users can add tasks, mark them as completed, and remove them. Let's get started!

Step 1: Setting Up the HTML

First, let's set up the basic structure of our to-do list in HTML. We'll need an input field for adding new tasks, a button for submitting the input, and an unordered list for displaying the tasks.

<!DOCTYPE html>
<html>
<head>
    <title>To-Do List</title>
</head>
<body>
    <input type="text" id="taskInput" placeholder="New task">
    <button id="addTaskButton">Add Task</button>
    <ul id="taskList"></ul>
</body>
</html>

Step 2: Adding Tasks

Next, let's use JavaScript to add tasks to the list. We'll select the input field and the button, and set up an event listener on the button that adds a new list item to the task list whenever the button is clicked.

let taskInput = document.querySelector('#taskInput');
let addTaskButton = document.querySelector('#addTaskButton');
let taskList = document.querySelector('#taskList');

addTaskButton.addEventListener('click', function() {
    let task = taskInput.value; // get the input value (the new task)
    let listItem = document.createElement('li'); // create a new list item
    listItem.textContent = task; // set the text content of the list item
    taskList.appendChild(listItem); // append the list item to the task list
    taskInput.value = ''; // clear the input field
});

Step 3: Marking Tasks as Completed

Now, let's add the ability to mark tasks as completed by clicking on them. We'll set up an event listener on the task list that checks if the target of a click event is a list item, and if so, toggles the 'completed' class on it.

taskList.addEventListener('click', function(event) {
    if (event.target.tagName === 'LI') {
        event.target.classList.toggle('completed');
    }
});

Step 4: Removing Tasks

Finally, let's allow users to remove tasks from the list. We'll add a 'Remove' button to each task, and set up an event listener on the task list that removes a task when its 'Remove' button is clicked.

addTaskButton.addEventListener('click', function() {
    // ...previous code...

    let removeButton = document.createElement('button');
    removeButton.textContent = 'Remove';
    listItem.appendChild(removeButton);
});

taskList.addEventListener('click', function(event) {
    if (event.target.tagName === 'BUTTON') {
        let task = event.target.parentElement;
        taskList.removeChild(task);
    }
});

Step 5: Add Some Style

body {
    font-family: Arial, sans-serif;
    padding: 20px;
}
#taskInput, #addTaskButton {
    margin: 10px 0;
}
#taskList {
    list-style-type: none;
    padding: 0;
}
#taskList li {
    background-color: #f9f9f9;
    border: 1px solid #ddd;
    margin-bottom: 10px;
    padding: 10px 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
#taskList li.completed {
    text-decoration: line-through;
}
button {
    background-color: #ff6347;
    color: white;
    border: none;
    padding: 5px 10px;
    cursor: pointer;
}
button:hover {
    background-color: #ee3120;
}

And there you have it! You've built a dynamic, interactive to-do list using the DOM manipulation techniques we've covered in this article. This is just a simple example, but the possibilities are endless. Keep practicing, keep experimenting, and most importantly, keep having fun! Happy coding!

P.S. If you have problems with your code, remember to check on the following:

  1. HTML Structure: Make sure your HTML structure matches the one used in the code. The IDs of the elements (taskInput, addTaskButton, taskList) should match exactly.
  2. JavaScript Placement: If your JavaScript code is in a <script> tag in the head of your HTML document, make sure to either move the <script> tag to the end of the body, or add the defer attribute to it. This ensures that the JavaScript code is executed after the HTML has been fully loaded, so it can access the elements in the DOM.
  3. Browser Console: Check the console in your browser's developer tools for any error messages. These messages can often provide clues about what's going wrong.
  4. CSS: If the 'completed' class is not visibly changing anything when a list item is clicked, make sure you have some CSS that targets .completed and changes the appearance of the element in some way (for example, by crossing out the text).

Conclusion

And there you have it! We've journeyed through the fascinating world of the Document Object Model (DOM), exploring its nooks and crannies, and uncovering its secrets. We've learned how to select elements, manipulate them using various properties and methods, handle events, and even got a sneak peek into the realm of asynchronous JavaScript.

We've discussed common pitfalls in DOM manipulation and how to avoid them, and we've put all our newfound knowledge to the test by building a dynamic, interactive to-do list. Along the way, we've seen how JavaScript breathes life into static HTML, transforming it into a vibrant, interactive experience.

But remember, this is just the beginning. The DOM is a vast and complex beast, and there's always more to learn. So don't stop here. Keep practicing, keep experimenting, and most importantly, keep having fun. Try adding new features to the to-do list, or start a new project from scratch. The more you code, the more comfortable you'll become with these concepts and techniques.

And stay tuned for future articles, where we'll dive deeper into asynchronous JavaScript and other advanced topics. The journey never ends, and every step you take brings you closer to becoming a master of web development.

Additional Resources

As we wrap up our journey through the DOM, you might be wondering where to go next. The world of web development is vast and ever-changing, and there's always more to learn. To help you continue your learning journey, here are some online resources that I highly recommend:

  1. MDN Web Docs: The Mozilla Developer Network provides a wealth of information on all things web development, including in-depth guides and reference materials on HTML, CSS, and JavaScript. Their JavaScript and DOM Manipulation guide is a great place to dive deeper into the topics we've covered.
  2. JavaScript.info: This site offers a modern JavaScript tutorial that covers everything from basic to advanced concepts, including a detailed section on DOM manipulation.
  3. W3Schools: W3Schools provides simple, straightforward tutorials on a wide range of web development topics. Their JavaScript HTML DOM tutorial is a great resource for beginners.
  4. Eloquent JavaScript: This book by Marijn Haverbeke provides a deep dive into JavaScript, including a chapter on The Document Object Model. It's available to read online for free.
  5. freeCodeCamp: freeCodeCamp offers interactive coding lessons and projects. Their JavaScript Algorithms and Data Structures certification includes a section on Basic Data Structures that can help you understand how to work with arrays and objects in JavaScript, which is crucial for DOM manipulation.
  6. Codecademy: Codecademy's Learn JavaScript course includes interactive lessons and projects that can help you practice the concepts you've learned.

Remember, the key to mastering web development is practice. Don't just read about these concepts—apply them. Build projects, solve problems, and don't be afraid to make mistakes. That's how you learn.

Happy coding!

Top comments (0)