A collection of interactive DOM manipulation projects demonstrating core JavaScript concepts for working with the Document Object Model.
The Document Object Model (DOM) is a programming interface for web documents. It represents the page as a tree structure where each HTML element is a node that can be accessed and manipulated using JavaScript.
Document
└── html
├── head
│ ├── title
│ └── link (stylesheets)
└── body
├── div
├── img
└── script
Location: Add Friend/
Concepts Demonstrated:
- Event listeners (
addEventListener) - DOM selection (
querySelector,getElementById) - Style manipulation (
element.style) - Text content modification (
innerHTML,innerText) - Boolean flags for state management
How it works:
- Uses a boolean
flagvariable to track friendship status - Toggles button text between "Add Friend" and "Remove Friend"
- Changes status text and color dynamically
- Demonstrates state management in vanilla JavaScript
Key Code:
addFriend.addEventListener("click", () => {
if (!flag) {
flag = true;
addFriend.innerHTML = "Remove Friend";
isStatus.innerText = "Friends";
isStatus.style.color = "Green";
} else {
flag = false;
addFriend.innerHTML = "Add Friend";
isStatus.innerText = "Stranger";
isStatus.style.color = "#999";
}
});Location: Custom Cursor/
Concepts Demonstrated:
- Mouse event tracking (
mousemove) - Position manipulation (
element.style.left,element.style.top) - Event object properties (
dets.x,dets.y) - CSS positioning (absolute positioning)
- Pointer events manipulation
How it works:
- Tracks mouse movement using
mousemoveevent - Updates cursor position based on mouse coordinates
- Uses
transform: translate(-50%, -50%)for perfect centering pointer-events: noneprevents cursor from interfering with mouse events
Key Code:
main.addEventListener("mousemove", (dets) => {
crsr.style.left = dets.x + "px";
crsr.style.top = dets.y + "px";
});Location: Insta Love Button/
Concepts Demonstrated:
- Double-click events (
dblclick) - Click events (
click) - CSS transforms (
scale,translate) - Timeout functions (
setTimeout) - Conditional styling
- CSS transitions
How it works:
- Double-clicking the image shows a large heart animation
- Single heart icon toggles between white and red on click
- Uses
transform: scale()for smooth heart animation - Automatically hides the big heart after 1 second using
setTimeout
Key Code:
post.addEventListener("dblclick", () => {
smallLove.style.color = "red";
love.style.transform = "translate(-50%, -50%) scale(1)";
setTimeout(() => {
love.style.transform = "translate(-50%, -50%) scale(0)";
}, 1000);
});Location: Insta Story/
Concepts Demonstrated:
- Dynamic HTML generation
- Array manipulation with
forEach - Template literals for HTML creation
- Event delegation
- Background image manipulation
- Timed visibility with
setTimeout - Data-driven UI
How it works:
- Creates story circles dynamically from an array of objects
- Each object contains a display picture (
dp) and story image - Clicking a story displays it full-screen for 3 seconds
- Uses
dets.target.idto identify which story was clicked - Demonstrates data-driven UI patterns
Key Code:
arr.forEach(function (elem, idx) {
slug += `<div id="story"> <img id="${idx}" src="${elem.dp}" /> </div>`;
});
strs.addEventListener("click", (dets) => {
fs.style.display = "block";
fs.style.backgroundImage = `url(${arr[dets.target.id].story})`;
setTimeout(function () {
fs.style.display = "none";
}, 3000);
});Location: Multi Image Hover/
Concepts Demonstrated:
querySelectorAllfor multiple elementsforEachloop for iteration- Multiple event listeners on same element
- Mouse enter/leave events
- Opacity manipulation
- Image positioning based on mouse coordinates
How it works:
- Selects all elements with class
.elem - Attaches three event listeners to each element:
mousemove: Moves image horizontally with cursormouseenter: Shows image (opacity: 1)mouseleave: Hides image (opacity: 0)
- Uses
querySelectorwithin each element to target specific child
Key Code:
elems.forEach((elem) => {
let elemImage = elem.querySelector("img");
elem.addEventListener("mousemove", (dets) => {
elemImage.style.left = dets.x + "px";
});
elem.addEventListener("mouseenter", () => {
elemImage.style.opacity = 1;
});
elem.addEventListener("mouseleave", () => {
elemImage.style.opacity = 0;
});
});Selects a single element by its ID attribute.
let element = document.getElementById("container");Used in: All projects
Selects the first element that matches a CSS selector.
let firstElem = document.querySelector(".elem");
let idElement = document.querySelector("#stories");Used in: Add Friend/script.js, Insta Story/script.js, Multi Image Hover/script.js
Selects all elements that match a CSS selector. Returns a NodeList.
let allElems = document.querySelectorAll(".elem");
// Returns: NodeList [element1, element2, element3, ...]Used in: Multi Image Hover/script.js
Gets or sets the HTML content inside an element.
element.innerHTML = "<h1>New Content</h1>";Used in: Add Friend/script.js, Insta Story/script.js
Gets or sets the visible text content (respects CSS styling).
element.innerText = "New Text";Used in: Add Friend/script.js
Gets or sets all text content (ignores CSS styling).
element.textContent = "Raw text content";Difference between innerText and textContent:
innerTextrespects CSS (won't show hidden text)textContentreturns all text regardless of styling
Directly modifies inline CSS styles.
element.style.color = "red";
element.style.fontSize = "20px";
element.style.backgroundColor = "#333";Used in: All projects
- CSS:
background-color→ JS:backgroundColor - CSS:
font-size→ JS:fontSize
The classList property provides methods to manipulate CSS classes on an element.
Adds one or more classes to an element.
element.classList.add("active");
element.classList.add("active", "highlighted", "important");Removes one or more classes from an element.
element.classList.remove("active");
element.classList.remove("active", "highlighted");Toggles a class (adds if absent, removes if present).
// If element has "active", it removes it
// If element doesn't have "active", it adds it
element.classList.toggle("active");Better approach for the Add Friend project:
addFriend.addEventListener("click", () => {
addFriend.classList.toggle("active");
isStatus.classList.toggle("friend-status");
});Checks if an element has a specific class (returns boolean).
if (element.classList.contains("active")) {
console.log("Element is active!");
}Replaces one class with another.
element.classList.replace("btn-primary", "btn-secondary");// Using style (inline CSS - hard to maintain)
element.style.color = "red";
element.style.fontSize = "20px";
element.style.fontWeight = "bold";
// Using classList (better practice)
element.classList.add("error-message");/* CSS file */
.error-message {
color: red;
font-size: 20px;
font-weight: bold;
}Benefits of classList:
- Separation of concerns (CSS stays in stylesheets)
- Easier to maintain and modify
- Can apply multiple styles at once
- Better performance
- Easier to debug
The modern way to attach event handlers to elements.
element.addEventListener("click", function() {
// Your code here
});
// Arrow function syntax
element.addEventListener("click", () => {
// Your code here
});click- Single clickdblclick- Double click (used in Insta Love Button/script.js)mousemove- Mouse movement (used in Custom Cursor/script.js, Multi Image Hover/script.js)mouseenter- Mouse enters element (used in Multi Image Hover/script.js)mouseleave- Mouse leaves element (used in Multi Image Hover/script.js)mousedown- Mouse button pressedmouseup- Mouse button releasedmouseover- Mouse over element (bubbles)mouseout- Mouse out of element (bubbles)
keydown- Key is pressedkeyup- Key is releasedkeypress- Key is pressed (deprecated, use keydown)
document.addEventListener("keydown", (e) => {
console.log(e.key); // Prints the key pressed
});submit- Form submittedinput- Input value changeschange- Input value changes and loses focusfocus- Element receives focusblur- Element loses focus
inputElement.addEventListener("input", (e) => {
console.log(e.target.value);
});load- Page fully loadedresize- Window resizedscroll- Page scrolled
window.addEventListener("scroll", () => {
console.log(window.scrollY);
});When an event occurs, JavaScript creates an event object with useful properties.
element.addEventListener("click", (event) => {
console.log(event.target); // Element that triggered event
console.log(event.type); // Type of event ("click")
console.log(event.clientX); // Mouse X coordinate
console.log(event.clientY); // Mouse Y coordinate
});Common Event Properties:
event.target- Element that triggered the eventevent.currentTarget- Element that the listener is attached toevent.type- Type of eventevent.clientX/event.clientY- Mouse coordinates relative to viewportevent.pageX/event.pageY- Mouse coordinates relative to documentevent.key- Key pressed (keyboard events)event.preventDefault()- Prevents default behaviorevent.stopPropagation()- Stops event from bubbling
Used in projects:
- Custom Cursor/script.js:
dets.x,dets.y - Multi Image Hover/script.js:
dets.x - Insta Story/script.js:
dets.target.id
Used for creating dynamic HTML strings with embedded expressions.
let name = "John";
let age = 25;
// Old way (concatenation)
let html = "<div>" + name + " is " + age + " years old</div>";
// New way (template literals)
let html = `<div>${name} is ${age} years old</div>`;Multi-line templates:
let card = `
<div class="card">
<h1>${title}</h1>
<p>${description}</p>
</div>
`;Used in: Insta Story/script.js
Executes code after a specified delay (in milliseconds).
setTimeout(() => {
console.log("Runs after 2 seconds");
}, 2000);Used in: Insta Love Button/script.js, Insta Story/script.js
Executes code repeatedly at specified intervals.
let counter = 0;
let intervalId = setInterval(() => {
counter++;
console.log(counter);
if (counter === 5) {
clearInterval(intervalId); // Stops the interval
}
}, 1000);Executes a function for each array element.
let numbers = [1, 2, 3, 4, 5];
numbers.forEach((num, index) => {
console.log(`Index ${index}: ${num}`);
});Used in: Insta Story/script.js, Multi Image Hover/script.js
Creates a new array by transforming each element.
let numbers = [1, 2, 3, 4, 5];
let doubled = numbers.map(num => num * 2);
// Result: [2, 4, 6, 8, 10]Creates a new array with elements that pass a test.
let numbers = [1, 2, 3, 4, 5];
let evenNumbers = numbers.filter(num => num % 2 === 0);
// Result: [2, 4]Reduces array to a single value.
let numbers = [1, 2, 3, 4, 5];
let sum = numbers.reduce((total, num) => total + num, 0);
// Result: 15Instead of adding event listeners to multiple elements, add one listener to a parent element.
// ❌ Inefficient
buttons.forEach(button => {
button.addEventListener("click", handleClick);
});
// ✅ Efficient (Event Delegation)
container.addEventListener("click", (e) => {
if (e.target.matches("button")) {
handleClick(e);
}
});Used in: Insta Story/script.js - clicks on parent #stories instead of individual story elements.
Custom attributes to store data on elements.
<div data-user-id="123" data-role="admin">User Info</div>let element = document.querySelector("div");
console.log(element.dataset.userId); // "123"
console.log(element.dataset.role); // "admin"
element.dataset.status = "active";let div = document.createElement("div");
div.className = "card";
div.innerHTML = "<h1>Title</h1>";
document.body.appendChild(div);// appendChild - adds single Node, returns Node
parent.appendChild(childElement);
// append - adds multiple items (Nodes/strings), no return value
parent.append(child1, child2, "text");element.insertAdjacentHTML("beforeend", "<div>New Content</div>");Positions:
"beforebegin"- Before the element"afterbegin"- Inside, before first child"beforeend"- Inside, after last child"afterend"- After the element
// Remove element
element.remove();
// Remove child
parent.removeChild(child);
// Clear all children
element.innerHTML = "";
// or
element.replaceChildren();let rect = element.getBoundingClientRect();
console.log(rect.width); // Width
console.log(rect.height); // Height
console.log(rect.top); // Distance from top of viewport
console.log(rect.left); // Distance from left of viewport
// Scroll position
console.log(window.scrollY); // Vertical scroll
console.log(window.scrollX); // Horizontal scroll
// Element size including padding
console.log(element.offsetWidth);
console.log(element.offsetHeight);
// Element size excluding padding
console.log(element.clientWidth);
console.log(element.clientHeight);These CSS properties work well with JavaScript DOM manipulation:
/* Smooth transitions */
.element {
transition: all 0.3s ease;
}
/* Transforms */
.element {
transform: translate(50px, 100px); /* Move */
transform: scale(1.5); /* Scale */
transform: rotate(45deg); /* Rotate */
transform: skew(10deg, 20deg); /* Skew */
}
/* Combining transforms */
.element {
transform: translate(50px, 100px) scale(1.5) rotate(45deg);
}Used in: Insta Love Button/style.css, [Custom Cursor/style.css](Custom Cursor/style.css)
When an event occurs on an element, it propagates through the DOM tree.
Bubbling (default): Event travels from target → parent → grandparent → document
child.addEventListener("click", () => {
console.log("Child clicked");
});
parent.addEventListener("click", () => {
console.log("Parent clicked"); // Also fires when child is clicked
});Stop bubbling:
child.addEventListener("click", (e) => {
e.stopPropagation();
console.log("Only child, parent won't fire");
});Capturing (rare): Event travels from document → grandparent → parent → target
element.addEventListener("click", handler, true); // true = capture phase// ❌ Avoid
var element = document.getElementById("card");
// ✅ Preferred
const element = document.getElementById("card");// ❌ Inefficient (queries DOM multiple times)
document.getElementById("btn").style.color = "red";
document.getElementById("btn").style.fontSize = "20px";
// ✅ Efficient (queries once)
const btn = document.getElementById("btn");
btn.style.color = "red";
btn.style.fontSize = "20px";// ❌ Verbose
element.style.color = "red";
element.style.fontSize = "20px";
element.style.fontWeight = "bold";
// ✅ Clean
element.classList.add("error-state");See Event Delegation section above.
function handleClick() {
console.log("Clicked");
}
element.addEventListener("click", handleClick);
// Later...
element.removeEventListener("click", handleClick);// ❌ Vulnerable to XSS attacks
element.innerHTML = userInput;
// ✅ Safe (renders as text, not HTML)
element.textContent = userInput;- Start with: Add Friend/ - Basic events and styling
- Then try: Custom Cursor/ - Mouse tracking
- Next: Insta Love Button/ - Multiple events and animations
- Advanced: Insta Story/ - Dynamic content generation
- Master: Multi Image Hover/ - Working with multiple elements
- Add Friend: index.html | script.js | style.css
- Custom Cursor: index.html | script.js | style.css
- Insta Love Button: index.html | script.js | style.css
- Insta Story: index.html | script.js | style.css
- Multi Image Hover: index.html | script.js | style.css
After mastering these concepts, explore:
- Local Storage - Persist data in browser
- Fetch API - Make HTTP requests
- Async/Await - Handle asynchronous operations
- Web Components - Create reusable custom elements
- Intersection Observer - Detect element visibility
- Animation Libraries - GSAP, Anime.js
- Frameworks - React, Vue, Svelte (build on these DOM concepts)
Happy Coding! 🎉