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.
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...'.
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.
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.
What is event bubbling?
What method finds the nearest ancestor matching a CSS selector?