Web Development /JavaScript Programming / Lesson
Web Development

JavaScript DOM Manipulation and Dynamic Content

Learn how to use JavaScript to change text, HTML, attributes, classes, styles and page structure dynamically through the Document Object Model.

🎯

Learning Objectives

["Understand the difference between selecting a DOM element and manipulating it.","Change text content using textContent.","Understand when to use innerHTML and the risks associated with inserting untrusted HTML.","Read and modify HTML attributes using getAttribute() and setAttribute().","Use removeAttribute() to remove attributes dynamically.","Work with data attributes using dataset.","Change CSS classes with classList.","Add, remove and toggle classes dynamically.","Change inline styles through the style property.","Create new DOM elements with createElement().","Add elements using append(), appendChild(), prepend() and before().","Remove elements from the DOM.","Build dynamic lists and interface components with JavaScript.","Understand safe and maintainable DOM manipulation practices."]

📖

Lesson Content

Introduction

In the previous lesson, you learned how JavaScript finds elements in an HTML document using the DOM.

Finding an element is only the beginning. The real power of the DOM becomes visible when JavaScript can change the webpage dynamically.

DOM manipulation means using JavaScript to modify the structure, content, attributes, classes or styles of elements on a webpage.

For example:

const title = document.querySelector('#title');

title.textContent = 'Welcome to Dwallnet Technologies';

JavaScript has now changed the text displayed by the HTML element without requiring the browser to reload the page.

What Is DOM Manipulation?

DOM manipulation is the process of using JavaScript to interact with and modify elements in the document.

JavaScript can:

  • Change text.
  • Change HTML content.
  • Change attributes.
  • Read attributes.
  • Remove attributes.
  • Add and remove CSS classes.
  • Change styles.
  • Create new elements.
  • Insert new elements.
  • Remove existing elements.
  • Build dynamic lists and components.

These capabilities are essential for modern interactive websites and web applications.

Changing Text with textContent

The textContent property allows JavaScript to read or replace the text contained inside an element.

HTML:

<h1 id="title">Old Title</h1>

JavaScript:

const title = document.querySelector('#title');

title.textContent = 'New Title';

The browser now displays:

New Title

This is one of the safest and most commonly used ways to update text dynamically.

Reading textContent

You can also read the text inside an element.

const title = document.querySelector('#title');

console.log(title.textContent);

This can be useful when JavaScript needs to process information already displayed on the page.

textContent Replaces Existing Content

When you assign a new value to textContent, the existing child content is replaced with text.

For example:

<div id="message">
    <strong>Welcome</strong>
</div>

Then:

const message = document.querySelector('#message');

message.textContent = 'Hello Student';

The existing HTML structure inside the element is replaced by the new text.

innerHTML

The innerHTML property allows JavaScript to read or replace the HTML markup inside an element.

Example:

const message = document.querySelector('#message');

message.innerHTML = '<strong>Welcome Student</strong>';

The browser interprets the supplied string as HTML.

This makes innerHTML useful when you intentionally need to create or replace markup.

textContent vs innerHTML

These properties behave differently.

textContent treats the assigned value as text.

element.textContent = '<strong>Hello</strong>';

The browser displays the characters rather than interpreting them as HTML.

innerHTML interprets the value as HTML:

element.innerHTML = '<strong>Hello</strong>';

The text appears bold because the browser creates the <strong> element.

Security and innerHTML

innerHTML must be used carefully when dealing with information that comes from users, external systems or other untrusted sources.

Inserting untrusted HTML directly into a page can create cross-site scripting (XSS) vulnerabilities.

For ordinary text, prefer:

element.textContent = userInput;

rather than:

element.innerHTML = userInput;

When HTML really needs to be inserted, it should be generated from trusted or properly sanitized data.

Changing Attributes

HTML elements contain attributes such as:

  • id
  • class
  • href
  • src
  • alt
  • title
  • disabled
  • data-*

JavaScript can read and modify these attributes.

getAttribute()

The getAttribute() method reads the value of an attribute.

HTML:

<a id="profile-link" href="/student-dashboard/">
    Dashboard
</a>

JavaScript:

const link = document.querySelector('#profile-link');

const destination = link.getAttribute('href');

console.log(destination);

setAttribute()

The setAttribute() method creates or changes an attribute.

const link = document.querySelector('#profile-link');

link.setAttribute('href', '/courses/');

The link now points to the new location.

You can also set a title:

link.setAttribute('title', 'View Courses');

Changing an Image

JavaScript can dynamically change an image source.

HTML:

<img id="student-photo" src="student.jpg" alt="Student">

JavaScript:

const image = document.querySelector('#student-photo');

image.setAttribute('src', 'profile.jpg');

Changing the alternative text is also possible:

image.setAttribute('alt', 'Student profile photo');

removeAttribute()

The removeAttribute() method removes an attribute from an element.

For example:

const button = document.querySelector('#submit-button');

button.removeAttribute('disabled');

This removes the disabled attribute.

Checking for an Attribute

The hasAttribute() method checks whether an element contains a particular attribute.

const button = document.querySelector('#submit-button');

if (button.hasAttribute('disabled')) {
    console.log('Button is disabled.');
}

Working With Data Attributes

HTML provides custom data attributes using the data- prefix.

Example:

<button
    class="course-button"
    data-course-id="101"
    data-course-slug="web-development"
>
    Continue Course
</button>

JavaScript can access these values using dataset.

const button = document.querySelector('.course-button');

console.log(button.dataset.courseId);
console.log(button.dataset.courseSlug);

Notice that:

data-course-id

becomes:

dataset.courseId

Changing Data Attributes

You can also modify a data attribute through dataset.

button.dataset.courseStatus = 'completed';

This creates:

data-course-status="completed"

CSS Classes and classList

Modern interfaces frequently change their appearance by adding or removing CSS classes.

JavaScript provides the classList API for this purpose.

Consider:

<div id="message" class="message">
    Welcome
</div>

You can inspect the classes:

const message = document.querySelector('#message');

console.log(message.classList);

classList.add()

The add() method adds a class.

message.classList.add('success');

The element now contains:

class="message success"

classList.remove()

The remove() method removes a class.

message.classList.remove('success');

classList.toggle()

The toggle() method adds a class if it is missing and removes it if it already exists.

message.classList.toggle('active');

This is especially useful for menus, tabs, modals, dropdowns and dark-mode controls.

classList.contains()

The contains() method checks whether an element has a particular class.

if (message.classList.contains('success')) {
    console.log('Success message is active.');
}

Adding Multiple Classes

You can add multiple classes in one call.

message.classList.add('success', 'visible', 'highlighted');

Similarly:

message.classList.remove('highlighted', 'visible');

Changing Inline Styles

JavaScript can also modify an element’s inline CSS styles using the style property.

Example:

const title = document.querySelector('#title');

title.style.fontSize = '32px';

Another example:

title.style.marginBottom = '20px';

CSS properties containing hyphens are written in camelCase in JavaScript.

For example:

background-color

becomes:

backgroundColor

And:

border-radius

becomes:

borderRadius

Using classList Instead of Excessive Inline Styles

Although the style property is useful, large interfaces are often easier to maintain when visual states are controlled by CSS classes.

Instead of:

element.style.backgroundColor = 'blue';
element.style.color = 'white';
element.style.padding = '20px';

You can define a CSS class:

.active-card {
    background-color: blue;
    color: white;
    padding: 20px;
}

Then JavaScript only needs:

element.classList.add('active-card');

This keeps presentation in CSS and behavior in JavaScript.

Creating New Elements

JavaScript can create new HTML elements dynamically using document.createElement().

Example:

const paragraph = document.createElement('p');

The variable now contains a new paragraph element that has not yet been inserted into the page.

You can give it content:

paragraph.textContent = 'Welcome to the course.';

Adding a Class to a New Element

paragraph.classList.add('course-message');

The element is now ready to be inserted into the DOM.

appendChild()

The appendChild() method adds a node to the end of another element’s children.

HTML:

<div id="container"></div>

JavaScript:

const container = document.querySelector('#container');

const paragraph = document.createElement('p');

paragraph.textContent = 'New content';

container.appendChild(paragraph);

The resulting HTML is approximately:

<div id="container">
    <p>New content</p>
</div>

append()

The append() method can also add content to the end of an element.

container.append(paragraph);

Unlike appendChild(), append() can accept multiple nodes and text values.

container.append(
    paragraph,
    'Additional text'
);

prepend()

The prepend() method inserts content at the beginning of an element.

container.prepend(paragraph);

before()

The before() method inserts content immediately before an element.

container.before(paragraph);

after()

The after() method inserts content immediately after an element.

container.after(paragraph);

Removing an Element

An element can remove itself from the DOM using the remove() method.

const message = document.querySelector('#message');

message.remove();

This removes the element from the document.

Removing a Child With removeChild()

The parent element can also remove one of its children using removeChild().

const container = document.querySelector('#container');

const paragraph = container.querySelector('p');

container.removeChild(paragraph);

The modern remove() method is usually simpler when you already have a reference to the element.

Building a Dynamic List

One of the most useful DOM manipulation techniques is dynamically generating a list.

HTML:

<ul id="course-list"></ul>

JavaScript:

const courses = [
    'Web Development',
    'Data Analysis',
    'Cyber Security'
];

const list = document.querySelector('#course-list');

courses.forEach(course => {

    const item = document.createElement('li');

    item.textContent = course;

    list.append(item);
});

JavaScript has created a new list item for every course.

Building Dynamic Course Cards

DOM manipulation can be used to create complete components.

const card = document.createElement('article');

card.classList.add('course-card');

const title = document.createElement('h2');

title.textContent = 'JavaScript';

const description = document.createElement('p');

description.textContent =
    'Learn modern JavaScript development.';

const button = document.createElement('button');

button.textContent = 'Start Course';

card.append(
    title,
    description,
    button
);

document.body.append(card);

This creates an entire course card from JavaScript.

Using Document Fragments

When many elements need to be created, a DocumentFragment can help organize DOM insertion.

const fragment = document.createDocumentFragment();

courses.forEach(course => {

    const item = document.createElement('li');

    item.textContent = course;

    fragment.append(item);
});

list.append(fragment);

The fragment allows multiple elements to be assembled before being inserted into the document.

Changing Multiple Elements

You can combine querySelectorAll() with DOM manipulation.

const cards = document.querySelectorAll('.course-card');

cards.forEach(card => {
    card.classList.add('loaded');
});

Every selected card receives the loaded class.

Dynamic Student Dashboard Example

Imagine a student dashboard containing a progress indicator.

HTML:

<div class="progress-card">

    <h2 id="course-name">Web Development</h2>

    <p id="progress-text">Progress: 0%</p>

    <div id="progress-bar"></div>

</div>

JavaScript:

const progress = 65;

const progressText =
    document.querySelector('#progress-text');

const progressBar =
    document.querySelector('#progress-bar');

progressText.textContent =
    `Progress: ${progress}%`;

progressBar.style.width =
    `${progress}%`;

The interface can now display a dynamically calculated progress value.

Dynamic Class States

Instead of changing styles directly, you can use CSS classes to represent states.

For example:

const progressCard =
    document.querySelector('.progress-card');

progressCard.classList.add('in-progress');

CSS can then control the visual appearance of the state.

Replacing Content

Sometimes an entire section needs to be replaced.

For trusted static HTML:

container.innerHTML = `
    <h2>Course Complete</h2>
    <p>Congratulations!</p>
`;

Template literals make this convenient, but the same security considerations about untrusted content still apply.

Building Elements Without innerHTML

For dynamic or untrusted text, creating elements directly can provide better control.

const heading = document.createElement('h2');

heading.textContent = studentName;

const message = document.createElement('p');

message.textContent = 'Welcome to your dashboard.';

container.replaceChildren(
    heading,
    message
);

This approach treats the values as text rather than interpreting them as HTML.

replaceChildren()

The replaceChildren() method removes the existing children of an element and replaces them with the supplied nodes.

container.replaceChildren(
    heading,
    message
);

This is useful when completely replacing the contents of a component.

Cloning Elements

Sometimes an existing element can be copied using cloneNode().

const card = document.querySelector('.course-card');

const copy = card.cloneNode(true);

document.body.append(copy);

The true argument tells JavaScript to clone the element’s descendants as well.

Checking DOM Manipulation Results

Browser developer tools are extremely useful when working with the DOM.

You can inspect the Elements panel to see whether JavaScript correctly changed:

  • Text content.
  • HTML structure.
  • Attributes.
  • Classes.
  • Styles.

You can also use:

console.log(element);

to inspect the selected element.

Common DOM Manipulation Mistakes

1. Manipulating a Null Element

const button = document.querySelector('#missing');

button.textContent = 'Click';

If the element does not exist, this will cause an error.

Use an existence check when appropriate:

if (button) {
    button.textContent = 'Click';
}

2. Confusing textContent and innerHTML

Use textContent for text.

Use innerHTML only when intentionally working with HTML markup and the content is trusted or properly sanitized.

3. Replacing Classes Incorrectly

This:

element.className = 'active';

replaces the existing class list.

Often it is safer to use:

element.classList.add('active');

when you only need to add one state.

4. Creating an Element but Never Appending It

const item = document.createElement('li');

item.textContent = 'Web Development';

The element exists in memory but will not appear on the page until it is inserted into the DOM.

For example:

list.append(item);

DOM Manipulation Best Practices

  • Select elements carefully and use meaningful selectors.
  • Use textContent when inserting ordinary text.
  • Use innerHTML carefully and never blindly insert untrusted HTML.
  • Prefer CSS classes for reusable visual states.
  • Use classList instead of repeatedly replacing the entire class attribute.
  • Use createElement() when building structured dynamic content.
  • Check that required elements exist before manipulating them when their presence is uncertain.
  • Keep DOM manipulation code organized around clear components.
  • Avoid unnecessary repeated DOM operations in large interfaces.
  • Use browser developer tools to inspect the resulting DOM.

Practical LMS Example

Suppose a student completes a lesson. JavaScript could update the interface dynamically.

const lessonStatus =
    document.querySelector('.lesson-status');

lessonStatus.textContent = 'Completed';

lessonStatus.classList.remove('pending');

lessonStatus.classList.add('completed');

The interface can immediately reflect the new lesson state without reloading the page.

A progress value could also be updated:

const progressText =
    document.querySelector('.progress-text');

progressText.textContent = 'Progress: 75%';

This is the foundation for interactive dashboards, LMS platforms, admin panels and other modern web applications.

Mini Practical Project

Create a dynamic course list using JavaScript.

HTML:

<section>

    <h2>My Courses</h2>

    <ul id="course-list"></ul>

</section>

JavaScript:

const courses = [
    'Web Development',
    'WordPress Development',
    'Data Analysis',
    'Cyber Security'
];

const courseList =
    document.querySelector('#course-list');

courses.forEach(course => {

    const item = document.createElement('li');

    item.textContent = course;

    item.classList.add('course-item');

    courseList.append(item);
});

JavaScript dynamically creates all four list items.

Making the List More Informative

You can create buttons inside each course item.

courses.forEach(course => {

    const item = document.createElement('li');

    const title = document.createElement('span');

    title.textContent = course;

    const button = document.createElement('button');

    button.textContent = 'Continue';

    item.append(title, button);

    courseList.append(item);
});

This demonstrates how JavaScript can create nested DOM structures.

Summary

DOM manipulation allows JavaScript to transform a static HTML document into an interactive interface.

You learned how to:

  • Change text with textContent.
  • Work with HTML using innerHTML.
  • Read attributes with getAttribute().
  • Change attributes with setAttribute().
  • Remove attributes with removeAttribute().
  • Work with custom data attributes through dataset.
  • Add, remove and toggle CSS classes with classList.
  • Change inline styles through the style property.
  • Create elements with createElement().
  • Insert elements using append(), appendChild() and prepend().
  • Remove elements using remove().
  • Build dynamic lists and components.

The next major step is learning how JavaScript responds to user actions. This leads directly into events and event handling.

💻

Code Example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DOM Manipulation Practice</title>

    <style>
        .course-item {
            margin-bottom: 10px;
        }

        .completed {
            text-decoration: line-through;
            opacity: 0.6;
        }
    </style>
</head>
<body>

    <h1 id="student-name">Student Dashboard</h1>

    <p id="status">Learning JavaScript</p>

    <ul id="course-list"></ul>

    <script>
        const studentName =
            document.querySelector('#student-name');

        const status =
            document.querySelector('#status');

        const courseList =
            document.querySelector('#course-list');

        studentName.textContent =
            'Welcome to Dwallnet Technologies';

        status.classList.add('active');

        const courses = [
            'Web Development',
            'Data Analysis',
            'Cyber Security',
            'WordPress Development'
        ];

        courses.forEach(course => {

            const item = document.createElement('li');

            item.classList.add('course-item');

            item.textContent = course;

            courseList.append(item);
        });

        const firstCourse =
            courseList.querySelector('.course-item');

        if (firstCourse) {
            firstCourse.classList.add('completed');
        }
    </script>

</body>
</html>
⭐

Key Takeaways

["DOM manipulation allows JavaScript to change webpages dynamically.","textContent is generally preferred for inserting ordinary text safely.","innerHTML interprets strings as HTML and should be used carefully with untrusted data.","getAttribute() reads an attribute while setAttribute() creates or changes one.","removeAttribute() removes an attribute from an element.","dataset provides convenient access to data-* attributes.","classList provides methods for adding, removing, toggling and checking CSS classes.","The style property can modify inline CSS values.","createElement() creates new DOM elements in memory.","append(), appendChild(), prepend(), before() and after() insert content into the DOM.","remove() and removeChild() can remove elements from the DOM.","Dynamic DOM manipulation is a foundation of interactive websites and web applications.","Separating JavaScript behavior from CSS presentation improves maintainability.","Always consider security when inserting dynamic content into the DOM."]

Enjoying this lesson?

Sign in to your Dwallnet Academy student account to track your learning progress and mark lessons as complete.

Sign In to Track Progress