Skip to main content

Command Palette

Search for a command to run...

Advanced DOM Manipulation – Building Dynamic Interfaces

Updated
6 min readView as Markdown

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.

100 Days of Code: My Journey to Becoming a Full Stack Developer

Part 1 of 50

Welcome to my 100 Days of Code journey! In this series, I'll document my daily progress as I learn Full Stack Web Development from the ground up. Every post will cover what I learned, challenges I faced, mistakes I made, and the projects I built. My goal is not just to complete 100 days but to become a better developer through consistency, discipline, and learning in public. Topics I'll cover include: • Git & GitHub • HTML, CSS & JavaScript • React.js • Node.js & Express • MongoDB • APIs • Real-world Projects • AI tools for Developers Whether you're just starting out or revising your fundamentals, I hope this journey helps you learn alongside me. Let's build, learn, and grow together! 🚀

More from this blog

T

TheSaurceCode

73 posts

Documenting my journey to becoming a Full Stack Developer through daily blogs, coding challenges, projects, tutorials, and lessons learned. Learn, build, and grow with me