DOM Manipulation & Events

Select, modify, and create DOM elements with JavaScript, and respond to user actions through event listeners — the core of interactive web UIs.

Medium 60m 3 tasks
Prerequisites: JavaScript Basics

Learning Objectives

  • Select DOM elements with querySelector and querySelectorAll
  • Modify element content, attributes, and styles
  • Create and append new elements dynamically
  • Add event listeners for user interactions
  • Understand event bubbling and delegation

Selecting Elements

// Single element (first match)
const btn   = document.querySelector("#submit-btn");
const input = document.querySelector("input[type='text']");
const card  = document.querySelector(".card");

// All matching elements (NodeList)
const links = document.querySelectorAll("nav a");
const items = document.querySelectorAll(".port-row");

// Iterate NodeList
items.forEach(item => console.log(item.textContent));
// Or convert to array
const arr = Array.from(items);

Reading & Modifying Content

const el = document.querySelector("#status");

// Read
el.textContent     // plain text (safe)
el.innerHTML       // HTML markup (risky with user data!)
el.value           // for inputs

// Write (always prefer textContent for user data)
el.textContent = "Scan complete";
el.innerHTML   = "<strong>Open</strong>";  // only safe HTML

// Attributes
el.getAttribute("data-port")
el.setAttribute("data-port", "443")
el.removeAttribute("disabled")
el.dataset.port    // shortcut for data-* attributes

Modifying Styles & Classes

const el = document.querySelector(".port-row");

// classList (preferred over style for toggling)
el.classList.add("open");
el.classList.remove("closed");
el.classList.toggle("highlight");
el.classList.contains("open");   // true/false
el.classList.replace("pending", "done");

// Inline style (for dynamic values)
el.style.color        = "#22c55e";
el.style.display      = "none";
el.style.setProperty("--accent", "#ff0");

Creating & Inserting Elements

// Create
const row = document.createElement("tr");
const td  = document.createElement("td");

// Build
td.textContent = "443";
row.appendChild(td);

// Insert
const tbody = document.querySelector("tbody");
tbody.appendChild(row);         // add to end
tbody.prepend(row);             // add to start
tbody.insertBefore(row, ref);   // before a specific node

// Cleaner: insertAdjacentHTML (be careful with user input!)
tbody.insertAdjacentHTML("beforeend", `<tr><td>22</td><td>SSH</td></tr>`);

// Remove
row.remove();
tbody.removeChild(row);

// Clone
const copy = row.cloneNode(true); // true = deep clone

Event Listeners

const btn = document.querySelector("#scan-btn");

// Add listener
btn.addEventListener("click", function(event) {
  event.preventDefault();        // stop default behavior
  event.stopPropagation();       // stop bubbling
  console.log("Clicked!", event.target);
});

// Arrow function
btn.addEventListener("click", (e) => {
  const host = document.querySelector("#host").value.trim();
  if (!host) return;
  startScan(host);
});

// Common events
// "click", "dblclick", "mouseenter", "mouseleave"
// "keydown", "keyup", "keypress"
// "submit", "change", "input", "focus", "blur"
// "DOMContentLoaded", "load", "resize", "scroll"

Event Bubbling & Delegation

Events bubble up the DOM tree:

button clicked
  → div hears it
    → main hears it
      → body hears it
        → document hears it

Event delegation: attach ONE listener to a parent instead of many to children:

// BAD: listener on every row
document.querySelectorAll(".port-row").forEach(row => {
  row.addEventListener("click", handleClick);
});

// GOOD: delegation — one listener handles all rows
document.querySelector("tbody").addEventListener("click", (e) => {
  const row = e.target.closest(".port-row");
  if (!row) return;
  const port = row.dataset.port;
  showPortDetails(port);
});

DOMContentLoaded

// Run after DOM is parsed (not waiting for images/CSS)
document.addEventListener("DOMContentLoaded", () => {
  initApp();
});

// Or use defer on script tag — no event listener needed

Mini Project – Live Port Table

document.addEventListener("DOMContentLoaded", () => {
  const form  = document.querySelector("#scan-form");
  const tbody = document.querySelector("#results tbody");

  form.addEventListener("submit", async (e) => {
    e.preventDefault();
    const host = document.querySelector("#host").value.trim();

    // Simulate scan results
    const ports = [22, 80, 443];
    tbody.innerHTML = "";   // clear table

    for (const port of ports) {
      const row = document.createElement("tr");
      row.innerHTML = `
        <td>${port}</td>
        <td class="status open">OPEN</td>
      `;
      tbody.appendChild(row);
    }
  });

  // Event delegation on results
  tbody.addEventListener("click", (e) => {
    const row = e.target.closest("tr");
    if (row) row.classList.toggle("selected");
  });
});

Add a button to your scan-results page. Using JS, on click: change the page background to #0a0a0a, add class 'active' to the button, and change the h1 text to 'Scan Running...'.

✦ Answer the questions to complete this task

What method selects the first matching DOM element?

What property safely sets text content without parsing HTML?

Given an array of {port, status} objects, write a function renderTable(results) that creates elements dynamically and appends them to a . Add a green class for open ports and red for closed.

✦ Answer the questions to complete this task

What method creates a new HTML element?

What method adds an element as the last child?

Instead of adding a click listener to each row, add ONE listener to the . When a row is clicked, toggle a 'selected' class on it. Use e.target.closest('tr') to find the row.

✦ Answer the questions to complete this task

What is event bubbling?

What method finds the nearest ancestor matching a CSS selector?

💪 Exercises & Challenges

📝 MCQ Medium +20 XP

DOM & Events MCQ

DOM & Events MCQ

Start →
⚙️ Practical Medium +30 XP

Interactive Scan Dashboard

Interactive Scan Dashboard

Start →
🚩 Challenge Hard +50 XP

XSS via DOM

XSS via DOM

Start →