DOM and events
Selecting, safe updates, events and event delegation.
Updated
What the DOM is
The DOM (Document Object Model) is the page represented as a tree of objects that JavaScript can read and change. The browser builds it from the HTML; when you change it from JS, the page updates.
documenthtmlheadbodyheadermainulli"Apple"li"Pear"
The HTML is the initial text; the DOM is the live state of the page. If JS adds an <li>, it shows up in the DOM (and in DevTools → Elements), but not in "View source".
Node types
| Node | Example |
|---|---|
document |
the root |
| element | <li>, <button> |
| text | the text inside an element |
| comment | <!-- --> |
In practice you work almost only with elements.
Selecting
| Method | Returns | When |
|---|---|---|
querySelector(css) |
the first element or null |
the default — accepts any CSS selector |
querySelectorAll(css) |
a (static) NodeList |
several elements; [...list] to use map |
getElementById(id) |
one element | fast, by id |
el.closest(css) |
the nearest ancestor (or the element itself) | event delegation |
Changing things
el.textContent = 'Safe text' // ✓ text, doesn't interpret HTML
el.innerHTML = '<b>HTML</b>' // ⚠️ interprets HTML — XSS with user data
el.classList.add('active') // .remove / .toggle / .contains
el.dataset.id // reads data-id="..."
el.setAttribute('aria-expanded', 'true')
el.style.setProperty('--progress', '40%')
parent.append(child); el.remove()textContent vs innerHTML: the first is for text (safe), the second for trusted HTML. The difference is security — see XSS.
Events
An event is a signal that something happened: a click, a key, a submit, a scroll. You listen to it with a listener:
button.addEventListener('click', event => {
console.log(event.target) // the element that was clicked
})| Kind | Examples |
|---|---|
| mouse / pointer | click, pointerdown, mouseenter |
| keyboard | keydown, keyup |
| form | submit, input, change, focus, blur |
| window | resize, scroll, load |
Propagation: capture and bubbling
A click on an <li> isn't just the <li>'s. The event travels down from document to the <li> (capture), then climbs back up (bubbling) through ul, main, body… Every listener along the way receives it.
| Property / method | Role |
|---|---|
event.target |
the element where it happened (the deepest one) |
event.currentTarget |
the element that has the listener |
event.preventDefault() |
stops the default behavior (a submit, following a link) |
event.stopPropagation() |
stops the climb (rarely needed) |
Event delegation
Thanks to bubbling, you put a single listener on the parent instead of one on every child:
list.addEventListener('click', e => {
const item = e.target.closest('[data-id]')
if (item) remove(item.dataset.id)
})It also works for elements added later, and it uses less memory.
Forms
form.addEventListener('submit', e => {
e.preventDefault() // no reload
const data = Object.fromEntries(new FormData(e.currentTarget))
})The connection to React
React builds and updates the DOM for you, declaratively: you describe what the UI should look like, React makes the changes. But onClick, event.target, preventDefault, FormData, bubbling — they're exactly the same APIs. React even uses event delegation at the app's root.
Summary
- The DOM = the page's live tree, changeable from JS.
querySelector+closest;textContentfor text,innerHTMLonly with trusted HTML.- Events bubble up → delegation with one listener on the parent.