Frontend Engineeringbeginner
JavaScript DOM Manipulation — Select, Create, and Handle Events
Learn to manipulate the DOM: select elements, create/remove nodes, handle events (click, submit, keyboard), event delegation, and build interactive UIs.
Asma HafeezApril 17, 20266 min read
javascriptdomeventsbrowser
JavaScript DOM Manipulation
The DOM (Document Object Model) is the browser's representation of your HTML as a tree of objects. JavaScript can read and modify it — that's how web pages become interactive.
Selecting Elements
JavaScript
// By ID (returns single element or null)
const title = document.getElementById("main-title");
// CSS selector — first match
const btn = document.querySelector(".submit-btn");
const nav = document.querySelector("nav > ul > li:first-child");
// CSS selector — all matches (returns NodeList, not Array)
const items = document.querySelectorAll(".card");
const inputs = document.querySelectorAll("input[type='text']");
// Convert NodeList to Array for full array methods
const itemsArray = Array.from(items);
// or: [...items]
// Relative selectors
const parent = element.parentElement;
const children = element.children; // HTMLCollection
const firstChild = element.firstElementChild;
const next = element.nextElementSibling;
const prev = element.previousElementSibling;
const closest = element.closest(".container"); // walks up the DOM treeReading and Modifying Content
JavaScript
const el = document.querySelector(".card");
// Text content
el.textContent; // get plain text (no HTML)
el.textContent = "Hello"; // set text (escapes HTML — safe)
// HTML content
el.innerHTML; // get HTML string
el.innerHTML = "<strong>Bold</strong>"; // set HTML (careful: XSS risk if user input)
el.outerHTML; // includes the element itself
// Form input values
const input = document.querySelector("#username");
input.value; // get current value
input.value = "Asma"; // set valueWorking with Attributes
JavaScript
const link = document.querySelector("a");
// Standard attributes
link.href;
link.href = "https://example.com";
// Any attribute
link.getAttribute("data-id"); // get
link.setAttribute("data-id", "42"); // set
link.removeAttribute("disabled"); // remove
link.hasAttribute("disabled"); // check
// Data attributes
const card = document.querySelector(".card");
card.dataset.userId; // reads data-user-id attribute
card.dataset.category = "electronics"; // sets data-categoryCSS Classes
JavaScript
const el = document.querySelector(".btn");
// classList API (preferred over className)
el.classList.add("active"); // add
el.classList.remove("disabled"); // remove
el.classList.toggle("open"); // add if absent, remove if present
el.classList.toggle("open", true); // force add
el.classList.toggle("open", false); // force remove
el.classList.contains("active"); // check
el.classList.replace("old", "new"); // replace
// Replace whole class string (rarely needed)
el.className = "btn btn-primary";Inline Styles
JavaScript
const el = document.querySelector(".box");
el.style.backgroundColor = "blue"; // camelCase for hyphenated properties
el.style.fontSize = "18px";
el.style.display = "none"; // hide
el.style.display = ""; // remove inline style (reverts to CSS)
// Reading computed styles (includes CSS file styles)
const styles = window.getComputedStyle(el);
styles.backgroundColor; // actual computed value
styles.fontSize;Creating and Removing Elements
JavaScript
// Create element
const div = document.createElement("div");
div.className = "card";
div.textContent = "New card";
// Create with innerHTML (faster for complex HTML)
const template = document.createElement("div");
template.innerHTML = `
<article class="card">
<h2>Title</h2>
<p>Description</p>
</article>
`;
const card = template.firstElementChild;
// Append to DOM
document.body.appendChild(div); // add as last child
document.body.prepend(div); // add as first child
parent.insertBefore(div, referenceNode); // insert before specific child
// Modern insert methods
parent.append(div, "text node"); // add multiple, accepts text
parent.before(div); // insert before parent
parent.after(div); // insert after parent
refNode.replaceWith(div); // replace reference node
// Remove
el.remove(); // modern, remove self
parent.removeChild(el); // old way
// Clone
const clone = el.cloneNode(true); // true = deep clone (includes children)Event Listeners
JavaScript
const btn = document.querySelector("#submit-btn");
// Add listener
btn.addEventListener("click", function(event) {
console.log("clicked!", event);
});
// Arrow function (modern)
btn.addEventListener("click", (e) => {
e.preventDefault(); // stop default behavior (form submit, link navigate)
console.log("x:", e.clientX, "y:", e.clientY);
});
// Remove listener — must pass the same function reference
function handleClick(e) { console.log("clicked"); }
btn.addEventListener("click", handleClick);
btn.removeEventListener("click", handleClick);
// Listen once (auto-removes after first trigger)
btn.addEventListener("click", handleClick, { once: true });Common Events
JavaScript
// Mouse events
el.addEventListener("click", handler);
el.addEventListener("dblclick", handler);
el.addEventListener("mouseenter", handler); // no bubbling
el.addEventListener("mouseleave", handler); // no bubbling
el.addEventListener("mouseover", handler); // bubbles
el.addEventListener("mouseout", handler); // bubbles
el.addEventListener("contextmenu", handler); // right click
// Keyboard events
document.addEventListener("keydown", (e) => {
console.log(e.key, e.code, e.ctrlKey, e.shiftKey);
if (e.key === "Enter") { /* submit */ }
if (e.key === "Escape") { /* close modal */ }
if (e.ctrlKey && e.key === "s") {
e.preventDefault();
saveDocument();
}
});
// Form events
form.addEventListener("submit", (e) => {
e.preventDefault(); // always prevent default on forms
const data = new FormData(form);
data.get("username");
});
input.addEventListener("input", (e) => {
// fires on every keystroke
console.log(e.target.value);
});
input.addEventListener("change", (e) => {
// fires when focus leaves after value changed
validateInput(e.target.value);
});
// Window/document events
window.addEventListener("load", () => { /* page fully loaded */ });
document.addEventListener("DOMContentLoaded", () => { /* DOM ready */ });
window.addEventListener("resize", () => { console.log(window.innerWidth); });
window.addEventListener("scroll", () => { console.log(window.scrollY); });Event Delegation
Instead of adding listeners to every item (expensive, doesn't work for dynamic items), add ONE listener to the parent.
JavaScript
// Without delegation — breaks for dynamically added items
document.querySelectorAll(".delete-btn").forEach(btn => {
btn.addEventListener("click", deleteItem); // won't work for future items
});
// With delegation — one listener handles all current AND future items
document.querySelector(".item-list").addEventListener("click", (e) => {
const deleteBtn = e.target.closest(".delete-btn");
if (!deleteBtn) return; // click was elsewhere in the list
const itemId = deleteBtn.dataset.id;
deleteItem(itemId);
});Practical Example: Todo List
JavaScript
const form = document.querySelector("#todo-form");
const input = document.querySelector("#todo-input");
const list = document.querySelector("#todo-list");
let todos = [];
form.addEventListener("submit", (e) => {
e.preventDefault();
const text = input.value.trim();
if (!text) return;
const todo = { id: Date.now(), text, done: false };
todos.push(todo);
input.value = "";
renderTodos();
});
// Event delegation for complete and delete
list.addEventListener("click", (e) => {
const id = Number(e.target.closest("li")?.dataset.id);
if (!id) return;
if (e.target.classList.contains("complete-btn")) {
todos = todos.map(t => t.id === id ? { ...t, done: !t.done } : t);
}
if (e.target.classList.contains("delete-btn")) {
todos = todos.filter(t => t.id !== id);
}
renderTodos();
});
function renderTodos() {
list.innerHTML = todos.map(todo => `
<li data-id="${todo.id}" class="${todo.done ? "done" : ""}">
<span>${todo.text}</span>
<button class="complete-btn">${todo.done ? "Undo" : "Done"}</button>
<button class="delete-btn">Delete</button>
</li>
`).join("");
}Key Takeaways
querySelector/querySelectorAll— the modern way to select elementstextContentfor setting text (safe),innerHTMLfor HTML (watch XSS)classList.add/remove/toggle— cleaner than manipulatingclassNameaddEventListener— always use this, notonclickattributes- Event delegation — one parent listener beats many child listeners
e.preventDefault()— stops form submit, link navigation, right-click menu
Enjoyed this article?
Explore the Frontend Engineering learning path for more.
Found this helpful?
Leave a comment
Have a question, correction, or just found this helpful? Leave a note below.