Skip to main content

Manipulating the DOM

Reading and updating text

Once you've selected an element, you can read and modify its text content.

textContent

textContent gets or sets the plain text content of an element:

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

// Read text
console.log(paragraph.textContent); // "Hello World"

// Update text
paragraph.textContent = 'New text';

// textContent ignores HTML tags
paragraph.textContent = '<strong>Bold</strong>'; // Displays as text, not HTML
// Result: "<strong>Bold</strong>" (shown as text, not rendered as HTML)

Use textContent when: You want plain text only (safer, prevents XSS attacks).

innerHTML

innerHTML gets or sets the HTML content of an element:

const div = document.querySelector('div');

// Read HTML
console.log(div.innerHTML); // "<p>Hello</p>"

// Update HTML
div.innerHTML = '<p>New paragraph</p>';

// innerHTML renders HTML
div.innerHTML = '<strong>Bold</strong>'; // Renders as bold text

Use innerHTML when: You need to insert HTML (but be careful with user input—see safety notes below).

Safety with innerHTML

Warning: Using innerHTML with user input can lead to XSS (Cross-Site Scripting) attacks:

// DANGEROUS: User input directly in innerHTML
const userInput = '<img src=x onerror="alert(\'XSS\')">';
element.innerHTML = userInput; // Executes malicious code!

// Safe: Use textContent or sanitize input
element.textContent = userInput; // Safe: displays as text

Best practice: Use textContent unless you specifically need HTML. If you must use innerHTML with user input, sanitize it first.

Attributes

HTML elements have attributes (id, class, src, href, etc.). You can read and modify them:

Reading attributes

const link = document.querySelector('a');

link.id; // Get ID
link.className; // Get class attribute
link.href; // Get href (full URL)
link.getAttribute('href'); // Get href (as written in HTML)

// For custom attributes (data-*)
link.dataset.userId; // Get data-user-id="123"
link.getAttribute('data-user-id'); // Alternative

Setting attributes

const img = document.querySelector('img');

// Direct property (for standard attributes)
img.src = 'new-image.jpg';
img.alt = 'New alt text';
img.id = 'myImage';

// Using setAttribute (works for any attribute)
img.setAttribute('src', 'new-image.jpg');
img.setAttribute('data-id', '123');
img.setAttribute('aria-label', 'Description');

Removing attributes

const element = document.querySelector('div');

element.removeAttribute('id');
element.removeAttribute('data-custom');

Data attributes

HTML5 data attributes (data-*) are commonly used to store custom data:

<div data-user-id="123" data-status="active">User</div>
const div = document.querySelector('div');

// Access via dataset (converts kebab-case to camelCase)
div.dataset.userId; // "123"
div.dataset.status; // "active"

// Or use getAttribute
div.getAttribute('data-user-id'); // "123"

Classes

Managing CSS classes is a common task. The classList property makes this easy:

classList.add()

Add one or more classes:

const element = document.querySelector('div');

element.classList.add('active');
element.classList.add('highlighted', 'visible'); // Multiple classes

classList.remove()

Remove one or more classes:

element.classList.remove('active');
element.classList.remove('highlighted', 'visible');

classList.toggle()

Add the class if it's not present, remove it if it is:

element.classList.toggle('active');  // Toggle on/off
element.classList.toggle('active', true); // Force add
element.classList.toggle('active', false); // Force remove

classList.contains()

Check if an element has a class:

if (element.classList.contains('active')) {
console.log('Element is active');
}

classList.replace()

Replace one class with another:

element.classList.replace('old-class', 'new-class');

Example: Toggle button state

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

button.addEventListener('click', function() {
button.classList.toggle('active');

if (button.classList.contains('active')) {
button.textContent = 'Active';
} else {
button.textContent = 'Inactive';
}
});

Inline styles

You can modify inline styles directly via the style property:

const element = document.querySelector('div');

// Set individual styles
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.fontSize = '18px';
element.style.display = 'none';

// Note: CSS properties use camelCase in JavaScript
// CSS: background-color → JavaScript: backgroundColor
// CSS: font-size → JavaScript: fontSize

Setting multiple styles

// Method 1: Set individually
element.style.color = 'red';
element.style.fontSize = '18px';

// Method 2: Use cssText (replaces all styles)
element.style.cssText = 'color: red; font-size: 18px;';

// Method 3: Object.assign
Object.assign(element.style, {
color: 'red',
fontSize: '18px'
});

Best practice: Generally, use CSS classes instead of inline styles. Only use inline styles for dynamic values (e.g., positioning based on calculations).

Creating elements

Create new elements dynamically:

// Create element
const newDiv = document.createElement('div');
const newParagraph = document.createElement('p');
const newButton = document.createElement('button');

// Set properties before adding to DOM
newDiv.textContent = 'Hello';
newDiv.className = 'container';
newParagraph.textContent = 'Paragraph text';

Creating with innerHTML

You can also create elements using innerHTML:

const container = document.querySelector('#container');
container.innerHTML = '<div class="item">New item</div>';

Note: This replaces all content. For appending, use insertAdjacentHTML:

container.insertAdjacentHTML('beforeend', '<div>New item</div>');

Appending and removing elements

Appending elements

Add elements to the DOM:

const parent = document.querySelector('#container');
const newDiv = document.createElement('div');
newDiv.textContent = 'New element';

// Add to end
parent.appendChild(newDiv);

// Add to beginning
parent.insertBefore(newDiv, parent.firstChild);

// Add after another element
parent.insertBefore(newDiv, parent.children[1]);

// Modern alternative: insertAdjacentElement
parent.insertAdjacentElement('beforeend', newDiv);

insertAdjacentHTML positions

// beforebegin - before the element
element.insertAdjacentHTML('beforebegin', '<div>Before</div>');

// afterbegin - inside, at the start
element.insertAdjacentHTML('afterbegin', '<div>First child</div>');

// beforeend - inside, at the end (like appendChild)
element.insertAdjacentHTML('beforeend', '<div>Last child</div>');

// afterend - after the element
element.insertAdjacentHTML('afterend', '<div>After</div>');

Removing elements

const element = document.querySelector('#toRemove');

// Remove from parent
element.parentElement.removeChild(element);

// Modern alternative (simpler)
element.remove();

Example: Dynamic list

function addListItem(text) {
const list = document.querySelector('#myList');
const newItem = document.createElement('li');
newItem.textContent = text;

// Add delete button
const deleteBtn = document.createElement('button');
deleteBtn.textContent = 'Delete';
deleteBtn.addEventListener('click', function() {
newItem.remove();
});

newItem.appendChild(deleteBtn);
list.appendChild(newItem);
}

addListItem('Item 1');
addListItem('Item 2');

Document fragments

When adding many elements, use document fragments for better performance:

// Without fragment (slow - triggers reflow each time)
const list = document.querySelector('#list');
for (let i = 0; i < 100; i++) {
const item = document.createElement('li');
item.textContent = `Item ${i}`;
list.appendChild(item); // Triggers reflow 100 times
}

// With fragment (fast - single reflow)
const list = document.querySelector('#list');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const item = document.createElement('li');
item.textContent = `Item ${i}`;
fragment.appendChild(item);
}
list.appendChild(fragment); // Single reflow

Fragments let you build elements in memory before adding them to the DOM.

Common patterns

Show/hide elements

// Method 1: display property
element.style.display = 'none'; // Hide
element.style.display = 'block'; // Show

// Method 2: visibility property (keeps space)
element.style.visibility = 'hidden'; // Hide
element.style.visibility = 'visible'; // Show

// Method 3: class-based (best practice)
element.classList.add('hidden'); // Hide (CSS: .hidden { display: none; })
element.classList.remove('hidden'); // Show

Updating multiple elements

// Update all matching elements
document.querySelectorAll('.item').forEach(item => {
item.textContent = 'Updated';
item.classList.add('highlighted');
});

Cloning elements

const original = document.querySelector('.template');
const clone = original.cloneNode(true); // true = deep clone
clone.id = 'new-id'; // Change ID to avoid duplicates
document.body.appendChild(clone);