Advanced DOM Manipulation – Building Dynamic Interfaces
Yesterday, I learned about Destructuring, Spread, and Rest Operators and how they make working with arrays, objects, and function arguments much cleaner.
Today, I went back to one of the most important parts of frontend JavaScript:
The DOM.
I focused on how JavaScript can dynamically create, update, remove, and manage HTML elements.
This is important because modern web applications constantly change what users see without reloading the entire page.
Let's dive in.
What is the DOM?
DOM stands for Document Object Model.
When a browser loads an HTML document, it creates a tree-like representation of the page.
For example:
Document
│
└── HTML
├── HEAD
└── BODY
├── H1
├── P
└── BUTTON
JavaScript can interact with this structure to dynamically change the webpage.
Selecting Elements
One of the first things we need to do is select elements from the DOM.
getElementById()
const title =
document.getElementById("title");
querySelector()
const title =
document.querySelector("#title");
We can also select classes:
const card =
document.querySelector(".card");
querySelectorAll()
When we need multiple elements:
const cards =
document.querySelectorAll(".card");
This returns a collection of matching elements.
Changing Text
We can change an element's text using:
title.textContent = "Hello JavaScript!";
For example:
<h1 id="title">
Old Title
</h1>
JavaScript:
const title =
document.querySelector("#title");
title.textContent =
"New Title";
Changing HTML
We can also modify the HTML inside an element using:
element.innerHTML = "<strong>Hello</strong>";
However, innerHTML should be used carefully when inserting untrusted user-controlled content because it can introduce security problems such as XSS.
When we only need text, textContent is generally safer and clearer.
Changing Styles
JavaScript can modify inline styles:
const title =
document.querySelector("#title");
title.style.fontSize = "40px";
Another example:
title.style.marginTop = "20px";
However, for larger applications, changing CSS classes is usually cleaner.
Working With Classes
We can use classList to manage CSS classes.
Add a class
element.classList.add("active");
Remove a class
element.classList.remove("active");
Toggle a class
element.classList.toggle("active");
Check whether a class exists
element.classList.contains("active");
This is extremely useful for interactive UI elements.
Creating Elements
JavaScript can create completely new HTML elements.
const paragraph =
document.createElement("p");
Then:
paragraph.textContent =
"Created with JavaScript!";
Adding Elements to the Page
We can append the element:
document.body.append(paragraph);
Or append it to a specific container:
const container =
document.querySelector("#container");
container.append(paragraph);
Creating a Dynamic List
Suppose we have:
<ul id="skills"></ul>
JavaScript:
const skills = [
"HTML",
"CSS",
"JavaScript",
"React"
];
const list =
document.querySelector("#skills");
skills.forEach(skill => {
const li =
document.createElement("li");
li.textContent = skill;
list.append(li);
});
Now the list is generated dynamically from JavaScript data.
Removing Elements
We can remove an element using:
element.remove();
For example:
const paragraph =
document.querySelector("p");
paragraph.remove();
This removes it from the DOM.
Creating Attributes
We can set attributes using:
element.setAttribute(
"data-id",
"123"
);
Read an attribute:
element.getAttribute("data-id");
Check whether it exists:
element.hasAttribute("data-id");
Remove it:
element.removeAttribute("data-id");
Using dataset
For custom data-* attributes, JavaScript provides dataset.
HTML:
<div
id="user"
data-user-id="123"
data-role="developer">
</div>
JavaScript:
const user =
document.querySelector("#user");
console.log(
user.dataset.userId
);
console.log(
user.dataset.role
);
This is useful for attaching small pieces of metadata to DOM elements.
Creating Elements From Data
Now we can combine what we've learned from previous days.
Suppose we have:
const users = [
{
name: "Saurabh",
role: "Developer"
},
{
name: "Rahul",
role: "Designer"
}
];
We can dynamically create user cards:
const container =
document.querySelector("#users");
users.forEach(user => {
const card =
document.createElement("div");
card.classList.add("card");
const name =
document.createElement("h2");
name.textContent = user.name;
const role =
document.createElement("p");
role.textContent = user.role;
card.append(name, role);
container.append(card);
});
This is a fundamental pattern in vanilla JavaScript.
Event Listeners on Dynamic Elements
We can also add events to elements we create.
const button =
document.createElement("button");
button.textContent =
"Click Me";
button.addEventListener(
"click",
() => {
console.log(
"Button clicked!"
);
}
);
document.body.append(button);
Now the dynamically created button behaves like any normal HTML element.
Event Delegation
When many elements have similar events, event delegation can be useful.
Suppose:
<ul id="users">
<li data-id="1">
Saurabh
</li>
<li data-id="2">
Rahul
</li>
</ul>
Instead of attaching a separate listener to every li, we can listen on the parent:
const users =
document.querySelector("#users");
users.addEventListener(
"click",
(event) => {
const item =
event.target.closest("li");
if (!item) return;
console.log(
item.dataset.id
);
}
);
This works because events bubble up through the DOM.
createElement() vs innerHTML
Both can be used to create dynamic content, but they have different characteristics.
createElement()
const p =
document.createElement("p");
p.textContent = "Hello";
Advantages:
More explicit
Safer for text content
Useful for constructing elements programmatically
innerHTML
container.innerHTML =
"<p>Hello</p>";
Advantages:
Convenient for larger HTML snippets
Can be concise
But inserting untrusted strings through innerHTML can create security vulnerabilities.
DOM + Array Methods
This is where today's lesson connects directly with Day 53.
We can use:
users
.filter(user => user.active)
.map(user => user.name);
and then render the result into the DOM.
This creates a powerful workflow:
Data
↓
filter()
↓
map()
↓
Create DOM Elements
↓
Append to Page
This is very similar to the data-driven rendering patterns I'll encounter in React.
Best Practices
✔ Use textContent when inserting plain text.
✔ Prefer CSS classes over excessive inline styles.
✔ Use classList to manage UI states.
✔ Use createElement() when programmatically constructing DOM elements.
✔ Be careful with innerHTML and never insert untrusted content into it without appropriate sanitization.
✔ Use event delegation when managing many similar dynamic elements.
✔ Keep data and rendering logic reasonably separated.
My Biggest Takeaway
Today, I learned how JavaScript can dynamically control the webpage.
I can now:
Select → Create → Modify → Append → Remove → Interact
I also connected today's DOM concepts with yesterday's functional programming:
Data → filter/map → DOM → UI
This is an important bridge toward React because React essentially gives developers a more structured way to describe how UI should be generated from application state.
