Sunday, August 17, 2025
0 comments

JavaScript Learning Path: From Zero to Hero – Chapter 5: DOM Manipulation

 

Introduction

Welcome to Chapter 5 of our JavaScript Learning Path: From Zero to Hero! After mastering data structures in Chapter 4, it’s time to bring your web pages to life with DOM manipulation. The Document Object Model (DOM) is the bridge between your JavaScript code and the HTML/CSS that users see, allowing you to dynamically update content, styles, and structure. In this chapter, we’ll cover selecting elements, changing content and styles, creating/removing elements, and traversing the DOM. We’ll build an interactive note-taking app to apply these concepts in a real-world context. Let’s make your web pages dynamic and engaging!


1. DOM Tree & Selectors

The DOM is a tree-like representation of an HTML document, where each element, attribute, and text node is an object you can manipulate with JavaScript.

Understanding the DOM Tree

  • Root: <html> element.

  • Nodes: Elements (e.g., <div>), text, attributes.

  • Hierarchy: Parent, child, and sibling relationships.

Selectors

Use selectors to target DOM elements:

  • getElementById(id): Selects an element by its unique id.

  • querySelector(selector): Selects the first element matching a CSS selector.

  • querySelectorAll(selector): Returns a NodeList of all matching elements.

Example: Selecting Elements

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>DOM Selectors</title>
</head>
<body>
  <h1 id="title">Welcome</h1>
  <p class="info">This is a note app.</p>
  <p class="info">Add notes below.</p>
  <script>
    const title = document.getElementById('title');
    console.log(title); // <h1 id="title">Welcome</h1>

    const firstInfo = document.querySelector('.info');
    console.log(firstInfo); // <p class="info">This is a note app.</p>

    const allInfo = document.querySelectorAll('.info');
    allInfo.forEach(p => console.log(p.textContent));
    // This is a note app.
    // Add notes below.
  </script>
</body>
</html>
  • Real-World Use: Selecting a form input or a specific section of a webpage.

  • Pros:

    • getElementById is fast for unique IDs.

    • querySelector is versatile, supporting CSS selectors (e.g., .class, #id, [data-attr]).

    • querySelectorAll handles multiple elements.

  • Cons:

    • querySelector can be slower for complex selectors.

    • getElementById requires unique IDs, limiting flexibility.

  • Best Practices:

    • Use getElementById for single, known IDs.

    • Use querySelector for CSS-like flexibility.

    • Cache selectors in variables to avoid repeated DOM queries.

  • Alternatives:

    • getElementsByClassName or getElementsByTagName for specific classes/tags.

    • Libraries like jQuery for simplified DOM selection (less common in modern JS).


2. Changing Content (innerText, innerHTML, value)

Modify element content to make pages dynamic:

  • innerText: Sets or gets text content, ignoring HTML tags.

  • innerHTML: Sets or gets HTML content, including tags.

  • value: Gets or sets the value of form inputs.

Example: Dynamic Note Display

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Change Content</title>
</head>
<body>
  <h1 id="title">My Notes</h1>
  <input type="text" id="noteInput" placeholder="Enter note">
  <button onclick="updateTitle()">Update Title</button>
  <div id="noteDisplay"></div>
  <script>
    function updateTitle() {
      const input = document.getElementById('noteInput');
      const title = document.getElementById('title');
      const noteDisplay = document.getElementById('noteDisplay');

      title.innerText = input.value || "My Notes"; // Update text
      noteDisplay.innerHTML = `<p><strong>Note:</strong> ${input.value}</p>`; // Add HTML
      input.value = ""; // Clear input
    }
  </script>
</body>
</html>
  • Real-World Use: Updating a user’s profile name or displaying form feedback.

  • Pros:

    • innerText is safe and simple for plain text.

    • innerHTML enables rich content with HTML.

    • value is essential for form interactions.

  • Cons:

    • innerHTML can introduce XSS (cross-site scripting) risks if user input isn’t sanitized.

    • innerText strips HTML, limiting formatting.

  • Best Practices:

    • Use innerText for text-only updates.

    • Sanitize user input before using innerHTML (e.g., with libraries like DOMPurify).

    • Validate value for form inputs to avoid empty submissions.

  • Alternatives:

    • textContent for faster text updates (includes hidden text).

    • Template literals with manual DOM creation for safer HTML updates.


3. Changing Styles (style, classList)

Modify an element’s appearance with:

  • style: Directly sets inline CSS properties.

  • classList: Manages CSS classes (add, remove, toggle).

Example: Highlight Notes

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Change Styles</title>
  <style>
    .highlight { background-color: yellow; padding: 5px; }
    .error { border: 2px solid red; }
  </style>
</head>
<body>
  <input type="text" id="noteInput" placeholder="Enter note">
  <button onclick="highlightNote()">Highlight Note</button>
  <p id="note">Sample note</p>
  <script>
    function highlightNote() {
      const input = document.getElementById('noteInput');
      const note = document.getElementById('note');

      if (input.value) {
        note.innerText = input.value;
        note.classList.add('highlight'); // Add class
        input.classList.remove('error');
        note.style.fontSize = '18px'; // Inline style
      } else {
        input.classList.add('error'); // Add error class
        note.classList.remove('highlight');
      }
    }
  </script>
</body>
</html>
  • Real-World Use: Highlighting active menu items or showing form validation errors.

  • Pros:

    • style is straightforward for one-off changes.

    • classList keeps styles in CSS, improving maintainability.

  • Cons:

    • Inline styles (style) override CSS and are hard to manage.

    • classList requires predefined CSS classes.

  • Best Practices:

    • Prefer classList for reusable styles.

    • Use camelCase for style properties (e.g., fontSize).

    • Avoid excessive inline styles; use CSS classes instead.

  • Alternatives:

    • CSS frameworks (e.g., Tailwind, Bootstrap) for predefined classes.

    • CSS-in-JS libraries (e.g., styled-components) for dynamic styling.


4. Creating & Removing Elements (appendChild, remove)

Dynamically add or remove elements to update the DOM:

  • appendChild(node): Adds a child element to a parent.

  • remove(): Removes an element from the DOM.

Example: Add/Remove Notes

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Create/Remove Elements</title>
</head>
<body>
  <input type="text" id="noteInput" placeholder="Add a note">
  <button onclick="addNote()">Add Note</button>
  <ul id="noteList"></ul>
  <script>
    function addNote() {
      const input = document.getElementById('noteInput');
      const noteList = document.getElementById('noteList');
      const noteText = input.value.trim();

      if (!noteText) {
        alert("Please enter a note");
        return;
      }

      const li = document.createElement('li');
      li.innerHTML = `${noteText} <button onclick="this.parentElement.remove()">Delete</button>`;
      noteList.appendChild(li);
      input.value = "";
    }
  </script>
</body>
</html>
  • Real-World Use: Adding tasks to a to-do list or removing items from a cart.

  • Pros:

    • appendChild is precise for adding elements.

    • remove is simple and direct.

  • Cons:

    • appendChild only adds one node at a time.

    • Older browsers may not support remove (use parentNode.removeChild instead).

  • Best Practices:

    • Create elements with createElement for clarity.

    • Use insertBefore or append for more flexibility.

    • Avoid inline event handlers (onclick); use addEventListener (covered later).

  • Alternatives:

    • insertAdjacentHTML for quick HTML insertion.

    • Frameworks like React for declarative DOM updates.


5. Traversing DOM (Parent, Child, Siblings)

Navigate the DOM to access related elements:

  • Parent: parentElement or parentNode.

  • Children: children, firstElementChild, lastElementChild.

  • Siblings: nextElementSibling, previousElementSibling.

Example: Navigate Note Hierarchy

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>DOM Traversal</title>
</head>
<body>
  <div id="container">
    <p class="note">Note 1</p>
    <p class="note">Note 2</p>
    <p class="note">Note 3</p>
  </div>
  <button onclick="traverseDOM()">Traverse DOM</button>
  <script>
    function traverseDOM() {
      const container = document.getElementById('container');
      const firstNote = container.firstElementChild;
      console.log("First note:", firstNote.textContent); // Note 1

      const secondNote = firstNote.nextElementSibling;
      console.log("Second note:", secondNote.textContent); // Note 2

      const parent = secondNote.parentElement;
      console.log("Parent ID:", parent.id); // container

      const children = container.children;
      console.log("Child count:", children.length); // 3
    }
  </script>
</body>
</html>
  • Real-World Use: Highlighting a parent container when a child is clicked or navigating form fields.

  • Pros:

    • Precise navigation for dynamic updates.

    • Intuitive for hierarchical structures.

  • Cons:

    • Can break if DOM structure changes unexpectedly.

    • Accessing deeply nested elements can be verbose.

  • Best Practices:

    • Check for null/undefined when traversing (e.g., if (element.nextElementSibling)).

    • Use children (HTML elements) over childNodes (includes text nodes).

  • Alternatives:

    • querySelector with relative selectors (e.g., .parent .child).

    • Frameworks like Vue for reactive DOM updates.


Interactive Example: Note-Taking App

Let’s build a note-taking app that uses DOM manipulation to add, delete, and highlight notes.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Note-Taking App</title>
  <style>
    body { font-family: Arial, sans-serif; padding: 20px; }
    input, button { padding: 10px; margin: 5px; }
    ul { list-style: none; padding: 0; }
    li { margin: 10px 0; padding: 10px; border: 1px solid #ccc; }
    .highlight { background-color: #ffeb3b; }
    .error { border: 2px solid red; }
  </style>
</head>
<body>
  <h1 id="appTitle">My Notes</h1>
  <input type="text" id="noteInput" placeholder="Enter a note">
  <button onclick="addNote()">Add Note</button>
  <button onclick="highlightAll()">Highlight All</button>
  <ul id="noteList"></ul>
  <script>
    const notes = [];

    function addNote() {
      const input = document.getElementById('noteInput');
      const noteList = document.getElementById('noteList');
      const noteText = input.value.trim();

      if (!noteText) {
        input.classList.add('error');
        return;
      }

      input.classList.remove('error');
      const note = { id: Date.now(), text: noteText, highlighted: false };
      notes.push(note);

      const li = document.createElement('li');
      li.innerHTML = `
        ${noteText}
        <button onclick="deleteNote(${note.id})">Delete</button>
        <button onclick="toggleHighlight(${note.id})">Toggle Highlight</button>
      `;
      noteList.appendChild(li);
      input.value = "";
    }

    function deleteNote(id) {
      const index = notes.findIndex(note => note.id === id);
      if (index !== -1) {
        notes.splice(index, 1);
        const li = document.querySelector(`li button[onclick='deleteNote(${id})']`).parentElement;
        li.remove();
      }
    }

    function toggleHighlight(id) {
      const note = notes.find(note => note.id === id);
      if (note) {
        note.highlighted = !note.highlighted;
        const li = document.querySelector(`li button[onclick='toggleHighlight(${id})']`).parentElement;
        li.classList.toggle('highlight', note.highlighted);
      }
    }

    function highlightAll() {
      const noteList = document.getElementById('noteList');
      const lis = noteList.children;
      for (let li of lis) {
        li.classList.add('highlight');
      }
      notes.forEach(note => note.highlighted = true);
    }
  </script>
</body>
</html>
  • How It Works:

    • Selectors: Uses getElementById and querySelector to target elements.

    • Content: Updates innerHTML for notes and value for input.

    • Styles: Toggles classList for highlighting and error states.

    • Create/Remove: Adds notes with appendChild and removes with remove.

    • Traversal: Uses parentElement and children for dynamic updates.

  • Why It’s Useful: Mimics note-taking apps like Google Keep or Evernote.


Best Standards for DOM Manipulation

  • Selectors: Cache DOM queries in variables for performance.

  • Content: Use innerText for text, sanitize innerHTML for safety.

  • Styles: Prefer classList over inline style for maintainability.

  • Creation/Removal: Use createElement for new elements; check for existence before removing.

  • Traversal: Validate nodes before accessing to avoid errors.

  • Performance: Minimize DOM operations; batch updates when possible.

  • Accessibility: Ensure dynamic content is accessible (e.g., ARIA attributes).


Conclusion

You’ve just mastered DOM manipulation in JavaScript! From selecting elements to updating content, styles, and structure, you can now create dynamic, interactive web pages. The note-taking app demonstrates how these techniques power real-world applications.

0 comments:

Featured Post

Master Angular 20 Basics: A Complete Beginner’s Guide with Examples and Best Practices

Welcome to the complete Angular 20 learning roadmap ! This series takes you step by step from basics to intermediate concepts , with hands...

Subscribe

 
Toggle Footer
Top