DOM Crash Course
Table of contents
- 1. Accessing Elements
- 2. Working with Attributes
- 3. Modifying Content
- 4. Styling Elements
- 5. Other Key Methods
- 6. Attribute vs. Property
- 7. Security & Best Practices
- Comparison Table: Content Properties
- Example: Dynamic Styling
- Projects can be done with this learnings
- 1. Interactive To-Do List
- 2. Live Character Counter
- 3. Dynamic Quiz App
- 4. Theme Switcher
- 5. Image Gallery with Modal
- 6. Real-Time Form Validation
- 7. Drag-and-Drop Task Board
- 8. Pomodoro Timer
- Bonus Challenges:
- Project: DOM Tree Navigator
- HTML Structure
- CSS Styling
- JavaScript Logic
- Key DOM Concepts Demonstrated
- How to Use
- Enhancement Ideas
- 1. Advanced Element Traversal
- 2. Advanced Insertion Methods
- 3. Attribute & Property Methods
- 4. Advanced Content Manipulation
- 5. Advanced Styling
- 6. Collection Handling
- 7. Advanced Project Idea: Dynamic Table Manager
- 8. Key Differences Table
- Best Practices
The Document Object Model (DOM) is a tree-like representation of a web page, created by the browser when an HTML document loads. It allows programming languages like JavaScript to interact with the page's structure, content, and styles dynamically. Each HTML element (e.g., <div>
, <p>
) becomes a node in the DOM tree, which can be manipulated programmatically.
Let's dive deep into accessing and manipulating HTML elements, attributes, content, and styling using JavaScript:
1. Accessing Elements
getElementById()
Selects an element by its
id
attribute.Returns a single element (since
id
is unique).
<div id="header">Header</div>
const header = document.getElementById('header');
getElementsByClassName()
Returns a live
HTMLCollection
of elements with a specific class.Use
[index]
to access individual elements.
<div class="item">Item 1</div>
<div class="item">Item 2</div>
const items = document.getElementsByClassName('item');
console.log(items[0].textContent); // "Item 1"
getElementsByTagName()
- Returns a live
HTMLCollection
of elements with the specified tag name.
<p>Paragraph 1</p>
<p>Paragraph 2</p>
const paragraphs = document.getElementsByTagName('p');
console.log(paragraphs.length); // 2
querySelector()
- Returns the first element matching a CSS-style selector.
const firstButton = document.querySelector('.btn'); // First element with class "btn"
const input = document.querySelector('input[type="text"]'); // First text input
querySelectorAll()
- Returns a static
NodeList
of all elements matching the selector.
const allButtons = document.querySelectorAll('.btn'); // All elements with class "btn"
allButtons.forEach(button => {
console.log(button.textContent);
});
2. Working with Attributes
setAttribute()
- Sets the value of an attribute.
const link = document.querySelector('a');
link.setAttribute('href', 'https://google.com');
link.setAttribute('target', '_blank');
getAttribute()
- Retrieves the value of an attribute.
const href = link.getAttribute('href'); // "https://google.com"
Direct Property Access
- Many attributes can be accessed directly as properties:
link.href = 'https://example.com'; // Updates the href attribute
link.id = 'myLink'; // Sets the id
console.log(link.id); // "myLink"
3. Modifying Content
textContent
- Safely sets/returns plain text inside an element (ignores HTML tags).
<div id="box"><span>Hello</span></div>
const box = document.querySelector('#box');
box.textContent = 'New text'; // Replaces inner HTML with plain text
console.log(box.textContent); // "New text"
innerText
- Similar to
textContent
, but CSS-aware (returns only visible text).
console.log(box.innerText); // "Hello" (if element is visible)
innerHTML
- Sets/returns HTML content (can introduce security risks like XSS).
box.innerHTML = '<strong>Bold text</strong>'; // Renders HTML
console.log(box.innerHTML); // "<strong>Bold text</strong>"
outerHTML
- Includes the element itself along with its content.
console.log(box.outerHTML); // "<div id="box"><strong>Bold text</strong></div>"
4. Styling Elements
Inline Styles (.style
)
- Modify inline styles using camelCase properties:
const div = document.querySelector('div');
div.style.backgroundColor = 'blue';
div.style.fontSize = '20px';
div.style.marginTop = '10px';
classList
for CSS Classes
- Add/remove/toggle classes without overwriting existing ones:
div.classList.add('active');
div.classList.remove('inactive');
div.classList.toggle('hidden');
5. Other Key Methods
createElement()
& appendChild()
- Create and add elements dynamically:
const newDiv = document.createElement('div');
newDiv.textContent = 'New Div';
document.body.appendChild(newDiv);
remove()
& removeChild()
- Remove elements:
newDiv.remove(); // Modern method
// Older method:
const parent = newDiv.parentElement;
parent.removeChild(newDiv);
6. Attribute vs. Property
Attributes: Defined in HTML (e.g.,
<input required>
).Properties: Live values in the DOM (e.g.,
input.checked
).
<input type="checkbox" id="check" checked>
const check = document.getElementById('check');
console.log(check.getAttribute('checked')); // "checked" (string)
console.log(check.checked); // true (boolean)
7. Security & Best Practices
Avoid
innerHTML
for untrusted content (risk of XSS attacks).Prefer
textContent
overinnerText
for performance.Cache frequently accessed elements:
const cachedElement = document.getElementById('header'); // Store in a variable
Comparison Table: Content Properties
Property | Returns | Notes |
textContent | Plain text (all content) | Fast, ignores HTML |
innerText | Visible text (CSS-aware) | Slower, skips hidden elements |
innerHTML | HTML content | Security risk if misused |
outerHTML | HTML of element + its content | Replaces the entire element |
Example: Dynamic Styling
const button = document.querySelector('.btn');
button.addEventListener('click', () => {
button.style.backgroundColor = 'red';
button.classList.toggle('clicked');
});
By mastering these methods, you can fully control the DOM to create dynamic, interactive web pages!
Projects can be done with this learnings
Here are 8 practical projects to solidify your DOM manipulation skills, ranging from beginner to intermediate level:
1. Interactive To-Do List
Tech: HTML, CSS, JavaScript
Features:
Add/delete tasks
Mark tasks as complete
Filter tasks (all/active/completed)
Local storage persistence
DOM Skills:createElement()
,appendChild()
querySelectorAll()
for task buttonsclassList.toggle()
for completed taskslocalStorage
integration
2. Live Character Counter
Tech: Text input, real-time updates
Features:
Display remaining characters as user types
Change color when limit is exceeded
DOM Skills:input
event listenertextContent
updatesConditional styling with
classList
3. Dynamic Quiz App
Tech: Forms, timers
Features:
Multiple-choice questions
Score tracking
Progress bar
Result summary
DOM Skills:forms
andsubmit
eventinnerHTML
for question renderingsetInterval()
for timers
4. Theme Switcher
Tech: CSS Variables, buttons
Features:
Toggle between light/dark themes
Custom color picker
Save preference to
localStorage
DOM Skills:style.setProperty()
for CSS variablesdata-*
attributes for theme storagechange
event listeners
5. Image Gallery with Modal
Tech: Grid layout, overlay
Features:
Thumbnail grid
Click to enlarge in modal
Previous/next navigation
Caption display
DOM Skills:click
event delegationdisplay: none/block
togglingdataset
for image metadata
6. Real-Time Form Validation
Tech: Regex, error messages
Features:
Instant feedback on input fields
Password strength meter
Submit button enable/disable
DOM Skills:keyup
/blur
eventsclassList.add()
for error statesDynamic
<span>
error messages
7. Drag-and-Drop Task Board
Tech: Drag API, flexbox
Features:
Columns (Todo/In Progress/Done)
Drag tasks between columns
Animation on drop
DOM Skills:draggable
attributedragstart
/dragend
eventsinsertBefore()
for repositioning
8. Pomodoro Timer
Tech: Counters, audio
Features:
Adjustable work/break durations
Circular progress indicator
Notification sound
DOM Skills:setTimeout()
/setInterval()
SVG/CSS animation updates
<audio>
element control
Bonus Challenges:
Add keyboard shortcuts to projects
Implement undo/redo functionality
Create a responsive mobile menu
Build a password visibility toggler
Each project will help you master specific DOM manipulation techniques while creating portfolio-worthy apps. Start simple (To-Do List) and gradually tackle more complex projects (Drag-and-Drop Board)! 🚀
Let's build a DOM Tree Explorer project that combines traversal methods (parent
, child
, sibling
) and element manipulation (append
, prepend
, insertBefore
). Users will interact with a visual tree structure and manipulate it using various DOM methods.
Project: DOM Tree Navigator
Features:
Visual tree structure with nodes
Highlight parent/children/siblings on click
Add elements at different positions
Full traversal controls
Real-time DOM updates
HTML Structure
<div class="container">
<div class="controls">
<button onclick="addChild()">Add Child</button>
<button onclick="addSibling('after')">Add Next Sibling</button>
<button onclick="addSibling('before')">Add Prev Sibling</button>
<button onclick="removeNode()">Remove Node</button>
</div>
<div id="tree" class="tree">
<div class="node root">Root</div>
</div>
</div>
CSS Styling
.tree {
padding: 20px;
}
.node {
padding: 10px;
margin: 5px;
border: 2px solid #333;
cursor: pointer;
background: #fff;
}
.selected {
background: #b3e5fc;
border-color: #039be5;
}
.children {
margin-left: 30px;
border-left: 2px dashed #ccc;
}
JavaScript Logic
let selectedNode = null;
// Initialize tree
document.querySelectorAll('.node').forEach(node => {
node.addEventListener('click', handleNodeClick);
});
function handleNodeClick(e) {
// Clear previous selection
document.querySelectorAll('.node').forEach(n => n.classList.remove('selected'));
// Set new selection
selectedNode = e.target;
selectedNode.classList.add('selected');
e.stopPropagation();
}
// Add child node
function addChild() {
if (!selectedNode) return;
const childrenContainer = selectedNode.nextElementSibling || createChildrenContainer();
const newNode = createNode('Child');
if (!selectedNode.nextElementSibling) {
selectedNode.parentNode.insertBefore(childrenContainer, selectedNode.nextSibling);
}
childrenContainer.appendChild(newNode);
}
// Add sibling node
function addSibling(position) {
if (!selectedNode || selectedNode.classList.contains('root')) return;
const newNode = createNode('Sibling');
position === 'after'
? selectedNode.parentNode.insertBefore(newNode, selectedNode.nextSibling)
: selectedNode.parentNode.insertBefore(newNode, selectedNode);
}
// Remove node
function removeNode() {
if (!selectedNode || selectedNode.classList.contains('root')) return;
const parent = selectedNode.parentNode;
parent.removeChild(selectedNode);
selectedNode = null;
}
// Helper: Create node element
function createNode(text) {
const node = document.createElement('div');
node.className = 'node';
node.textContent = text;
node.addEventListener('click', handleNodeClick);
return node;
}
// Helper: Create children container
function createChildrenContainer() {
const div = document.createElement('div');
div.className = 'children';
return div;
}
Key DOM Concepts Demonstrated
Traversal:
parentNode
: Access direct parentnextElementSibling
: Get next siblingpreviousElementSibling
: Get previous siblingchildren
: Access child elements
Insertion Methods:
appendChild()
: Add to end of childreninsertBefore()
: Insert at specific positionparentNode.insertBefore()
: Relative positioning
Element Manipulation:
classList
managementcreateElement()
for dynamic nodesEvent delegation for dynamic elements
Position Detection:
Checking for existing children containers
Handling edge cases (root node protection)
How to Use
Click any node to select it (blue highlight)
Use buttons to:
Add child nodes (nested under selected)
Add siblings before/after selected node
Remove nodes (except root)
Observe the tree structure updates in real-time
Enhancement Ideas
Add persistence using
localStorage
Implement drag-and-drop reorganization
Add undo/redo functionality
Show traversal paths with animation
Add context menu for operations
Here’s a deep dive into advanced DOM methods and techniques, including traversal, manipulation, and edge cases:
1. Advanced Element Traversal
Parent/Child Navigation
parentElement
vsparentNode
:const child = document.querySelector('.child'); console.log(child.parentElement); // Returns parent element (ignores non-element nodes) console.log(child.parentNode); // Returns any parent node (including text/comment nodes)
children
vschildNodes
:const parent = document.querySelector('.parent'); console.log(parent.children); // Live HTMLCollection of **element** children console.log(parent.childNodes); // NodeList of **all** nodes (text, comments, elements)
Sibling Navigation
nextElementSibling
vsnextSibling
:const node = document.querySelector('.node'); console.log(node.nextElementSibling); // Next element sibling (ignores text nodes) console.log(node.nextSibling); // Next node (could be text/comment)
previousElementSibling
:
Works likenextElementSibling
but in reverse direction.
2. Advanced Insertion Methods
insertAdjacentHTML()
Insert elements at specific positions relative to a node:
const refNode = document.querySelector('.reference');
refNode.insertAdjacentHTML('beforebegin', '<div>Before</div>'); // Outside, before
refNode.insertAdjacentHTML('afterbegin', '<div>First Child</div>'); // Inside, first
refNode.insertAdjacentHTML('beforeend', '<div>Last Child</div>'); // Inside, last
refNode.insertAdjacentHTML('afterend', '<div>After</div>'); // Outside, after
replaceWith()
& replaceChild()
Replace elements:
const oldDiv = document.querySelector('.old');
const newDiv = document.createElement('div');
// Modern method:
oldDiv.replaceWith(newDiv);
// Legacy method:
oldDiv.parentNode.replaceChild(newDiv, oldDiv);
cloneNode()
Create copies of elements:
const original = document.querySelector('.template');
const deepCopy = original.cloneNode(true); // Clone with children
const shallowCopy = original.cloneNode(false); // Clone without children
3. Attribute & Property Methods
hasAttribute()
& removeAttribute()
Check for and delete attributes:
const link = document.querySelector('a');
if (link.hasAttribute('target')) {
link.removeAttribute('target');
}
toggleAttribute()
Toggle boolean attributes:
const button = document.querySelector('button');
button.toggleAttribute('disabled'); // Adds if missing, removes if present
dataset
Property
Access custom data-*
attributes:
<div id="user" data-user-id="42" data-role="admin"></div>
const userDiv = document.getElementById('user');
console.log(userDiv.dataset.userId); // "42"
console.log(userDiv.dataset.role); // "admin"
4. Advanced Content Manipulation
insertAdjacentText()
Safely insert text without parsing HTML:
const div = document.querySelector('.content');
div.insertAdjacentText('beforeend', 'New text <b>ignored</b>');
outerHTML
Replacement
Replace an element including its own tags:
document.querySelector('.replace-me').outerHTML = '<span>New element</span>';
5. Advanced Styling
getComputedStyle()
Read final calculated styles:
const element = document.querySelector('.box');
const styles = window.getComputedStyle(element);
console.log(styles.backgroundColor); // "rgb(255, 0, 0)"
classList
Advanced Methods
const div = document.querySelector('.container');
div.classList.replace('old-class', 'new-class'); // Replace class
console.log(div.classList.contains('active')); // Check existence
6. Collection Handling
Convert Live Collections to Arrays
// For HTMLCollection:
const liveItems = document.getElementsByClassName('item');
const itemsArray = Array.from(liveItems);
// For NodeList (querySelectorAll):
const nodeList = document.querySelectorAll('.items');
const anotherArray = [...nodeList]; // Spread operator
Live vs Static Collections
// Live collection (updates automatically):
const live = document.getElementsByTagName('div');
// Static collection (snapshot):
const static = document.querySelectorAll('div');
7. Advanced Project Idea: Dynamic Table Manager
Features:
Add/remove rows and columns
Sort rows by clicking headers
Highlight parent/child cells
Undo/redo functionality
DOM Methods Used:
// Add row
function addRow() {
const newRow = document.createElement('tr');
newRow.innerHTML = '<td contenteditable></td>'.repeat(columnCount);
table.appendChild(newRow);
}
// Sort rows
function sortRows(columnIndex) {
const rows = Array.from(table.rows);
rows.sort((a, b) => a.cells[columnIndex].textContent.localeCompare(b.cells[columnIndex].textContent));
table.tBodies[0].append(...rows);
}
// Highlight hierarchy
table.addEventListener('click', (e) => {
const cell = e.target.closest('td');
if (cell) {
// Highlight all children of parent row
const parentRow = cell.parentElement;
Array.from(parentRow.children).forEach(td => td.classList.toggle('highlight'));
}
});
8. Key Differences Table
Method/Property | Use Case |
parentElement | When you specifically need an element parent |
parentNode | When you need any type of parent node (text/comment/element) |
append() | Modern method to add multiple nodes/text (supports multiple arguments) |
appendChild() | Legacy method for single node insertion |
textContent | Safe text manipulation (prevents XSS) |
innerHTML | When intentional HTML parsing is needed |
insertAdjacentHTML() | Precise HTML insertion without destroying existing content |
Best Practices
Batch DOM Changes: Use
DocumentFragment
for multiple insertions:const fragment = new DocumentFragment(); for (let i = 0; i < 100; i++) { const div = document.createElement('div'); fragment.appendChild(div); } container.appendChild(fragment);
Debounce Rapid Updates: For scroll/resize events, use:
window.addEventListener('resize', debounce(handleResize, 200));
Avoid Forced Synchronous Layouts:
// Bad: Causes layout thrashing element.style.width = '100px'; console.log(element.offsetWidth); element.style.height = '200px'; // Good: Batch reads/writes element.style.width = '100px'; element.style.height = '200px'; console.log(element.offsetWidth);