Table of Contents
Understanding the DOM tree.........................................3
Key concepts........................................................................... 3
Selecting elements.....................................................................4
Differences & tips.....................................................................4
Example................................................................................. 4
Changing content & styles dynamically...............................4
Style changes...........................................................................5
Security & performance tips.........................................................5
Creating, appending & removing elements...........................5
Batch updates: DocumentFragment................................................6
Cloning.................................................................................. 6
Example:................................................................................ 6
Handling attributes & classes.........................................6
Classes................................................................................... 6
Accessibility attributes...............................................................6
Events................................................................7
Event handler basics..................................................................7
Removing listeners....................................................................8
Event bubbling & capturing..........................................8
Example showing order..............................................................8
Event delegation technique...........................................8
Why use it...............................................................................9
Pattern................................................................................... 9
Form validation using JavaScript.....................................9
Built-in API.............................................................................9
1
Custom validation pattern..........................................................10
Live validation & accessibility....................................................10
Common pitfalls.....................................................................10
Interactive UI examples with DOM.................................10
1) To-Do list..........................................................................10
2) Live search filter with debounce..............................................11
3) Simple accessible Modal.......................................................12
Debugging, performance & best practices..........................13
Debugging............................................................................ 13
Performance.......................................................................... 13
Batch reads, then writes, or use a DocumentFragment..................................13
Accessibility......................................................................................13
Security............................................................................................14
Modern tips & cross-browser.................................................................14
Quick checklist & summary........................................14
2
MODULE 6: DOM MANIPULATION AND
EVENT HANDLING
Understanding the DOM tree
The DOM (Document Object Model) is a tree representation of the HTML document where
every node is an object. The browser parses HTML and creates a tree you can read and
manipulate with JavaScript.
Key concepts
Document: document is the root entry point (represents the whole page).
Nodes vs Elements:
o Node is any item in the tree (element nodes, text nodes, comment nodes, etc.).
o Element is a node that represents an HTML tag (e.g., <div>). Element nodes are the
ones you usually manipulate.
Hierarchy: [Link] → usually the <html> element.
[Link] → <body>.
Common properties:
o [Link] — numeric type (1 = element, 3 = text).
o [Link] — tag name (e.g., "DIV").
o [Link] / [Link].
o [Link] (only elements) vs [Link] (includes text nodes).
o firstElementChild, lastElementChild, nextElementSibling, previousElementSibling.
Live vs static collections:
o [Link]() / getElementsByClassName() return live
HTMLCollection (updates as DOM changes).
o [Link]() returns a static NodeList (snapshot).
Why it matters: text nodes, whitespace, and comments are nodes too — when
traversing, use children or firstElementChild if you only want element nodes.
Small traversal example
const root = [Link];
[Link]([Link]); // elements only
3
[Link]([Link]); // includes text nodes
[Link]([Link]); // first element inside body
Selecting elements
How to grab elements from the DOM.
[Link]('id')
o Returns a single element or null. Fast and reliable when you know the ID.
[Link]('class')
o Returns live HTMLCollection.
[Link]('tag')
o Returns live HTMLCollection.
[Link](selector)
o Returns the first matching element (CSS selector). Very flexible.
[Link](selector)
o Returns a static NodeList of all matches.
Differences & tips
Use getElementById for single ID lookups (fast).
Use querySelector/querySelectorAll for complex CSS selectors (.card > .title,
input[name="email"]).
Convert NodeList to array when you want array methods: const arr =
[Link]([Link]('li')) or [...[Link]('li')].
Avoid searching the whole document each time; restrict scope:
[Link]('.item').
Example
const btn = [Link]('submitBtn');
const firstInput = [Link]('form input');
const items = [Link]('.todo-item'); // NodeList
const itemsArray = [...items]; // now a real array
Changing content & styles dynamically
You’ll often change what’s inside elements or modify their styles.
4
Changing content
[Link] — sets/gets text content. Fast and safe (escapes HTML).
[Link] — similar but respects CSS and layout; slower and affected by
visibility. Use textContent for predictable behavior.
[Link] — interprets string as HTML (danger: XSS if you insert
unsanitized user input).
[Link](position, html) — inserts HTML without replacing
whole innerHTML. Positions: "beforebegin", "afterbegin", "beforeend", "afterend".
Style changes
[Link] = 'value' — sets inline styles (camelCase:
[Link] = 'red').
[Link] = '...' — set multiple inline styles.
[Link]('cls') / .remove() / .toggle() / .contains() — preferred for styling
changes via CSS classes.
[Link](element) — read computed styles (final values after CSS
cascade).
Security & performance tips
Avoid innerHTML with user content—sanitize or use safe methods (textContent,
createTextNode).
Prefer toggling classes (cheap and keeps JS/CSS separation) over repeatedly changing
many inline styles.
Example toggling a class
const panel = [Link]('.panel');
[Link]('is-open'); // adds/removes class, letting CSS handle visuals
Creating, appending & removing elements
APIs for building DOM nodes and placing them.
[Link]('tag') — creates an element.
[Link]('text') — creates a text node (rarely needed; textContent
often simpler).
[Link](node) — append as the last child (returns appended node).
[Link](node1, node2, 'text') — modern, accepts strings and multiple nodes.
5
[Link](node) — insert at the beginning.
[Link](newNode, referenceNode) — legacy insert at index.
[Link](newNode) — replace element directly.
[Link]() — remove node.
[Link](child) — older removal method.
Batch updates: DocumentFragment
const frag = [Link](); [Link](...);
[Link](frag);
Useful to minimize reflows: build the fragment in memory, then append once.
Cloning
[Link](true) — deep clone (with descendants). Useful for templates.
Example:
const ul = [Link]('#list');
const li = [Link]('li');
[Link] = 'New item';
[Link]('todo-item');
[Link](li);
Handling attributes & classes
Attributes vs properties
[Link]('data-id') vs [Link] — dataset is a nicer interface for
data-*.
Some properties behave differently from attributes: [Link] returns fully qualified URL,
while getAttribute('href') returns exactly what’s in HTML.
[Link]('aria-label', 'Close') / [Link]('disabled').
Classes
[Link]('x'), .remove('x'), .toggle('x'), .contains('x'), .replace('old','new').
[Link] = 'a b c' sets whole class string (overwrites), so classList is safer.
Accessibility attributes
Use role, aria-* attributes properly. For dynamic content, update aria-live, aria-
expanded, etc., so assistive tech knows the state.
6
Example: data- and ARIA
const btn = [Link]('.btn');
[Link]('aria-pressed', 'true');
[Link]([Link]); // reads data-user-id
Events
Events let you react to user interaction.
Common events and when they fire
click: mouse click or keyboard activation (on button when space/enter). Bubbles.
input: fires whenever the value of <input>/<textarea> changes (real-time). Great for
live search or validation.
change: for <input>/select — fires when control loses focus after value change (or
immediately for <select>). Useful when you don't want every keystroke.
submit: when a form is submitted. Use on form and [Link]() to
intercept.
keydown / keyup: fires on keyboard keys. Use these instead of keypress (deprecated).
keydown fires first and repeated while holding.
mouseover / mouseout: bubble and fire when mouse enters/leaves descendant
boundaries. mouseenter / mouseleave do not bubble (sometimes easier to reason
about).
pointerdown / pointerup / pointermove: unified pointer events (mouse, touch, stylus).
Event handler basics
[Link]('click', function(event) {
[Link](); // stop default browser action (e.g., link navigation)
[Link](); // stop event traveling upward
});
[Link] — where the event originated (could be a child).
[Link] — the element the handler is attached to.
this inside non-arrow function handler points to currentTarget.
addEventListener options: { capture: true } for capture phase, { once: true } to auto-
remove after one call, { passive: true } for scroll handlers (can't call
preventDefault()).
7
Removing listeners
Use named functions so you can remove them:
[Link]('click', namedFn).
Event bubbling & capturing
Event phases:
1. Capturing phase — event travels from the window down the DOM tree to the target
(capture listeners if capture:true).
2. Target phase — event reaches the target element (listeners attached without capture
are invoked here too).
3. Bubbling phase — event bubbles back up from target to document (listeners without
capture are fired during bubbling).
Example showing order
<div id="outer">
<div id="inner">
<button id="btn">Click</button>
</div>
</div>
[Link]('outer')
.addEventListener('click', () => [Link]('outer bubbled'), false);
[Link]('outer')
.addEventListener('click', () => [Link]('outer captured'), true);
Console when clicking #btn:
outer captured (capture down)
...target events...
outer bubbled (as event bubbles up)
Controls: [Link]() stops further bubbling/capturing.
stopImmediatePropagation() also prevents other listeners on the same element from running.
Event delegation technique
Instead of adding listeners to many child nodes, attach one listener to a common parent and
detect which child triggered it. Great for dynamic content (added later).
8
Why use it
Saves memory (fewer listeners).
Handles elements created after the listener was added.
Simpler management (one place to change behavior).
Pattern
<ul id="list">
<li data-id="1">A</li>
<li data-id="2">B</li>
</ul>
const ul = [Link]('list');
[Link]('click', e => {
const li = [Link]('li'); // finds the nearest <li> ancestor (including target)
if (!li || ) return; // click happened outside an li
[Link]('clicked item', [Link]);
});
Notes
Use [Link]() or [Link]() to test for the child type.
Be mindful of stopPropagation() on inner elements — it can block delegation if used
poorly.
Form validation using JavaScript
Two layers: HTML5 Constraint Validation API (built-in) + custom JS for UX. Always
validate on the server too.
HTML attributes
<input type="email" required minlength="5" pattern="^\\S+@\\S+\\.\\S+$">
Built-in API
[Link]() — returns true if every control passes constraints.
[Link]() — shows the browser’s default UI for invalid controls.
[Link]('message') — set a custom error; pass '' to clear.
[Link] — object with properties: valueMissing, typeMismatch,
patternMismatch, etc.
9
Custom validation pattern
[Link]('submit', e => {
[Link]();
if (![Link]()) {
[Link](); // shows native balloons
return;
}
// Do AJAX submit or other actions
});
Live validation & accessibility
Use input event to show live hints; mark invalid with aria-invalid="true" and link an
error message with aria-describedby.
For screen readers, use role="alert" or aria-live="polite" to announce messages added
dynamically.
Common pitfalls
Browser validation UI differs across browsers and can be limited; use custom UI for
consistent UX but still use constraint attributes for accessibility.
Don’t rely only on client validation — always validate server-side.
Avoid overly strict regexes for email/phone — prefer simple checks and server
validation.
Interactive UI examples with DOM
Below are compact examples for common interactive patterns. You can paste these into an
HTML file (with the shown CSS classes) to try them.
1) To-Do list
<!-- HTML -->
<form id="todoForm">
<input id="todoInput" placeholder="Add task" required />
<button type="submit">Add</button>
</form>
<ul id="todos"></ul>
const form = [Link]('todoForm');
const input = [Link]('todoInput');
const ul = [Link]('todos');
10
[Link]('submit', e => {
[Link]();
const text = [Link]();
if (!text) return;
const li = [Link]('li');
[Link] = 'false';
[Link] = `<span class="label">${text}</span>
<button class="done">Done</button>
<button class="delete">Delete</button>`;
[Link](li);
[Link] = '';
});
// Event delegation for buttons inside list
[Link]('click', e => {
const btn = [Link];
if ([Link]('.delete')) {
[Link]('li').remove();
} else if ([Link]('.done')) {
const li = [Link]('li');
[Link]('completed');
[Link] = [Link]('completed');
}
});
Why this is good: one listener on ul handles all child buttons; newly added items are handled
automatically.
2) Live search filter with debounce
<input id="search" placeholder="Search..." />
<ul id="list">
<!-- many li items -->
</ul>
function debounce(fn, wait) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), wait);
};
}
11
const search = [Link]('search');
const listItems = () => [Link]('#list li');
const filter = debounce(() => {
const q = [Link]().toLowerCase();
listItems().forEach(li => {
[Link] = [Link]().includes(q) ? '' : 'none';
});
}, 200);
[Link]('input', filter);
Debounce reduces work for every keystroke and improves perceived performance.
3) Simple accessible Modal
<button id="openModal">Open</button>
<div class="modal" id="modal" role="dialog" aria-modal="true" aria-hidden="true">
<div class="dialog">
<button id="close">Close</button>
<p>Modal content</p>
</div>
</div>
const openBtn = [Link]('openModal');
const modal = [Link]('modal');
const closeBtn = [Link]('close');
let lastFocused;
[Link]('click', () => {
lastFocused = [Link];
[Link]('aria-hidden', 'false');
[Link] = 'block';
[Link]();
});
[Link]('click', close);
[Link]('keydown', e => {
if ([Link] === 'Escape') close();
});
function close() {
12
[Link]('aria-hidden', 'true');
[Link] = 'none';
if (lastFocused) [Link]();
}
Notes: For production, implement focus trapping (keep focus inside modal) and ensure screen
reader announcements. This example shows basic behavior and keyboard support (Esc
closes).
Debugging, performance & best practices
Debugging
Use DevTools: Elements panel, Event Listeners panel, Console, Sources (set
breakpoints).
[Link](element) vs [Link](element) — dir shows JS properties.
Use debugger; to break in code.
Inspect listeners: right-click element → Inspect → Event Listeners.
Performance
Minimize layout thrashing: batch DOM reads before writes. Example bad pattern:
[Link] = '100px';
const w = [Link]; // causes reflow
[Link] = w + 'px';
Batch reads, then writes, or use a DocumentFragment.
Use classList toggles rather than setting many inline styles.
Use requestAnimationFrame() for animations and layout-related actions.
Debounce or throttle high-frequency events (input, scroll, resize).
Use passive: true for touchstart/wheel listeners when you don’t call preventDefault().
Accessibility
Always associate labels with inputs (<label for="id"> or wrap the input).
Keyboard users: ensure interactive elements are reachable with Tab and have visible
focus styles.
Use semantic HTML (buttons for actions, anchors for navigation).
13
Update ARIA attributes to reflect dynamic state: aria-expanded, aria-hidden, aria-
live.
Avoid role="button" on divs unless you implement keyboard behavior correctly.
Security
Never insert untrusted user input via innerHTML without sanitization.
Validate inputs server-side in addition to client-side.
Modern tips & cross-browser
Prefer addEventListener + modern APIs (classList, dataset, closest).
For very old browsers, polyfills may be needed (rare in 2025).
Use pointer events for unified touch/mouse handling.
Quick checklist & summary
Use semantic HTML and minimal JS for structure.
Use querySelector/querySelectorAll for flexible selections, getElementById for single
known IDs.
Use textContent for text, innerHTML only for trusted HTML.
Batch DOM updates and use DocumentFragment for many inserts.
Prefer classList for styling changes.
Use addEventListener with delegation where appropriate.
Use constraint validation + custom JS for UX — always validate server-side.
Make interactive components accessible (aria-*, keyboard, focus management).
Debug with DevTools, and optimize reflow/repaint.
14