What is the DOM?
The Document Object Model—the tree structure browsers build from HTML that JavaScript manipulates. Why 'document.getElementById' works.
document.getElementById('button'), you’re accessing the DOM. When JavaScript changes text on a page, it’s changing the DOM. React, Vue, and other frameworks are essentially tools for managing DOM updates efficiently.What the DOM is
When a browser loads HTML:
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1>Hello</h1>
<p>World</p>
</body>
</html>It builds a tree structure in memory:
document
└── html
├── head
│ └── title
│ └── "My Page"
└── body
├── h1
│ └── "Hello"
└── p
└── "World"This tree is the DOM. Each element is a “node” that JavaScript can access and modify.
Why it matters
The DOM is the interface between your code and what users see.
Without the DOM, JavaScript couldn’t:
- Change text on the page
- Show/hide elements
- Respond to clicks
- Create interactive experiences
With the DOM, JavaScript can do anything:
// Change content
document.querySelector('h1').textContent = 'Goodbye';
// Change styles
document.querySelector('p').style.color = 'red';
// Add elements
document.body.appendChild(document.createElement('div'));
// Remove elements
document.querySelector('p').remove();Basic DOM operations
Finding elements
// By ID (fastest, returns single element)
const header = document.getElementById('main-header');
// By CSS selector (returns first match)
const button = document.querySelector('.submit-btn');
// By CSS selector (returns all matches)
const items = document.querySelectorAll('.list-item');
// By class name
const cards = document.getElementsByClassName('card');
// By tag name
const paragraphs = document.getElementsByTagName('p');Changing content
// Change text
element.textContent = 'New text';
// Change HTML (be careful with user input—XSS risk)
element.innerHTML = '<strong>Bold text</strong>';
// Change attribute
element.setAttribute('href', 'https://example.com');
element.href = 'https://example.com'; // Shorthand for common attributes
// Change style
element.style.color = 'blue';
element.style.display = 'none';
// Change classes
element.classList.add('active');
element.classList.remove('hidden');
element.classList.toggle('visible');Creating and removing elements
// Create
const newDiv = document.createElement('div');
newDiv.textContent = 'I am new';
newDiv.className = 'my-class';
// Add to page
document.body.appendChild(newDiv);
parentElement.insertBefore(newDiv, referenceElement);
// Remove
element.remove();
parentElement.removeChild(childElement);Handling events
// Listen for clicks
button.addEventListener('click', () => {
console.log('Button clicked!');
});
// Listen for input
input.addEventListener('input', (event) => {
console.log('Value:', event.target.value);
});
// Listen for form submit
form.addEventListener('submit', (event) => {
event.preventDefault(); // Stop page reload
console.log('Form submitted');
});
// Remove listener
button.removeEventListener('click', handlerFunction);DOM vs HTML
HTML is static text:
<p id="greeting">Hello</p>The DOM is a live object that can change:
document.getElementById('greeting').textContent = 'Goodbye';
// The HTML file didn't change, but the DOM did
// User sees "Goodbye"The original HTML never changes. The DOM is a living representation that JavaScript modifies.
DOM and frameworks (React, Vue)
Frameworks exist because manual DOM manipulation is:
- Tedious: Lots of code to write
- Error-prone: Easy to forget updates
- Slow: Naive updates can hurt performance
Vanilla JavaScript approach
// Update when data changes
let count = 0;
const button = document.getElementById('counter');
const display = document.getElementById('count');
button.addEventListener('click', () => {
count++;
display.textContent = count; // Manual DOM update
});React approach
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<span>{count}</span> {/* React handles DOM update */}
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}React tracks state, calculates what changed, and updates only the necessary DOM nodes efficiently.
The Virtual DOM
React (and Vue) use a “virtual DOM”—a JavaScript object representing what the DOM should look like.
1. State changes
2. React builds new virtual DOM
3. React compares with previous virtual DOM
4. React calculates minimal changes needed
5. React applies only those changes to real DOMThis is faster than naively updating everything.
When you still need direct DOM access
Even with React, sometimes you need the real DOM:
Focus management
function SearchForm() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // Direct DOM access
}, []);
return <input ref={inputRef} />;
}Measuring elements
function Tooltip() {
const ref = useRef(null);
const [height, setHeight] = useState(0);
useEffect(() => {
setHeight(ref.current.offsetHeight); // Direct DOM measurement
}, []);
return <div ref={ref}>Content</div>;
}Third-party libraries
function Chart() {
const containerRef = useRef(null);
useEffect(() => {
// Library needs DOM element
new ChartLibrary(containerRef.current, data);
}, []);
return <div ref={containerRef} />;
}Common DOM gotchas
Element doesn’t exist yet
// This runs before HTML is parsed
const button = document.getElementById('my-button'); // null!
// Fix: wait for DOM to load
document.addEventListener('DOMContentLoaded', () => {
const button = document.getElementById('my-button'); // Works
});
// Or put script at end of body
// Or use defer attribute: <script defer src="app.js">NodeList isn’t an array
const items = document.querySelectorAll('.item');
items.forEach(item => { /* works */ });
items.map(item => item.textContent); // Error! Not an array
// Fix: convert to array
Array.from(items).map(item => item.textContent);
[...items].map(item => item.textContent);Changing innerHTML destroys event listeners
container.innerHTML = '<button>Click</button>';
// Old event listeners on container's children are gone!Live vs static collections
// getElementsByClassName returns LIVE collection
const items = document.getElementsByClassName('item');
// If you add/remove .item elements, collection updates automatically
// querySelectorAll returns STATIC collection
const items = document.querySelectorAll('.item');
// Collection won't change even if DOM changesBrowser DevTools and the DOM
The Elements tab in DevTools shows the DOM (not the original HTML):
- Right-click element → Inspect
- See the live DOM tree
- Edit elements directly
- Watch changes from JavaScript
Console access:
// In DevTools console
$0 // Currently selected element
$$('.class') // Shorthand for querySelectorAllQuick reference
// Find elements
document.getElementById('id')
document.querySelector('.class')
document.querySelectorAll('.class')
// Content
element.textContent = 'text'
element.innerHTML = '<b>html</b>'
// Attributes
element.setAttribute('name', 'value')
element.getAttribute('name')
element.removeAttribute('name')
// Classes
element.classList.add('class')
element.classList.remove('class')
element.classList.toggle('class')
element.classList.contains('class')
// Styles
element.style.property = 'value'
// Create/remove
document.createElement('div')
parent.appendChild(child)
element.remove()
// Events
element.addEventListener('click', handler)
element.removeEventListener('click', handler)
// Navigation
element.parentElement
element.children
element.firstElementChild
element.nextElementSiblingFurther reading
- What is JavaScript? : The language that manipulates the DOM
- What is SSR vs CSR? : How the DOM gets built
- How to debug your code : Using DevTools with the DOM
- Security basics for beginners : XSS and innerHTML dangers
Frequently asked questions