Chapter 1.6โ˜• 14 min read

DOM & Events

HTML ko JavaScript se control karo โ€” DOM ek baar samajh lo, phir kabhi bhoologe nahi.

01DOM โ€” What Is It?

The DOM (Document Object Model) is a programming interface that the browser creates when it loads your HTML. It converts every tag into a tree of objects that JavaScript can read and modify.

Think of it like this: HTML is the blueprint. The browser reads it and builds a live tree of JavaScript objects. That tree IS the DOM. Change the tree โ†’ the page updates instantly. No page reload needed.

Key DOM concepts:

// The entry point to everything
document           // the root object
document.documentElement  // <html> element
document.body      // <body> element
document.head      // <head> element

// Every tag becomes a "node"
// Element nodes: <div>, <p>, <span> etc.
// Text nodes: the text inside elements
// Comment nodes: <!-- comments -->
// Document node: the root itself

The DOM is not the HTML source code. It's a live representation. If JavaScript adds a new element, the DOM tree updates โ€” and so does what the user sees on screen.

02Selecting Elements

Before you can change anything, you need to find the element. Three main methods:

Rule of thumb: Use getElementById when you have an ID. Use querySelector for everything else (it accepts any CSS selector). Use querySelectorAll when you need multiple elements.
// 1. By ID โ€” fastest, returns ONE element or null
const header = document.getElementById('main-header');

// 2. By CSS selector โ€” returns FIRST match or null
const firstBtn = document.querySelector('.btn');
const deepItem = document.querySelector('nav ul li:first-child a');

// 3. All matches โ€” returns NodeList (NOT a real Array)
const allBtns = document.querySelectorAll('.btn');
const allParas = document.querySelectorAll('article > p');
๐Ÿ“‹ NodeList vs Array trap:
querySelectorAll returns a NodeList, not an Array. It has .forEach() but NOT .map(), .filter(), or .reduce(). To use array methods: Array.from(nodes) or [...nodes].
// This works โ€” NodeList has forEach
allBtns.forEach(btn => console.log(btn.textContent));

// This FAILS โ€” NodeList has no .map
allBtns.map(btn => btn.textContent); // TypeError!

// Fix: convert to real Array
[...allBtns].map(btn => btn.textContent); // works!
03Manipulating Content & Styles

Once you have an element reference, you can read and change its content, classes, and styles:

// TEXT CONTENT โ€” safe, no HTML parsing
el.textContent = 'Hello';          // sets text
el.textContent;                    // gets text (strips HTML tags)

// INNER HTML โ€” powerful but DANGEROUS
el.innerHTML = '<b>Bold</b>';    // renders as bold text
el.innerHTML;                       // gets HTML string

// CLASSES โ€” the modern way (no className string hacking)
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('open');      // add if missing, remove if present
el.classList.contains('active');  // returns true/false

// INLINE STYLES
el.style.color = 'red';
el.style.fontSize = '20px';
el.style.display = 'none';

// ATTRIBUTES
el.setAttribute('data-id', '42');
el.getAttribute('data-id');        // '42'
el.removeAttribute('data-id');
textContent vs innerHTML: textContent sets plain text โ€” even if you write <b>Hi</b>, the user sees the raw tags. innerHTML parses and renders HTML โ€” but if the content comes from user input, it's an XSS vulnerability. Rule: use textContent for user data, innerHTML only for trusted content you control.
04Creating & Removing Elements

You're not limited to modifying existing elements โ€” you can create new ones and remove old ones:

// CREATE a new element
const li = document.createElement('li');
li.textContent = 'New Item';
li.classList.add('item');

// ADD to the page
const ul = document.querySelector('ul');
ul.appendChild(li);               // adds at the end
ul.insertBefore(li, ul.firstChild); // adds at the beginning

// REMOVE
li.remove();                       // modern way โ€” removes itself
ul.removeChild(li);               // old way โ€” parent removes child
Bulk changes with innerHTML: For replacing all children at once, container.innerHTML = newHTML is faster than removing one-by-one. But again โ€” never use it with untrusted user input. For dynamic content you build yourself, it's fine.
// Fast way to replace all list items
const list = document.querySelector('ul');
list.innerHTML = items.map(item =>
  `<li class="item">${item.name}</li>`
).join('');
๐Ÿ“‹ Performance tip: Every DOM change can trigger a reflow (the browser recalculates layout). If you're adding 100 elements, build a DocumentFragment, append all elements to it, then append the fragment once โ€” only one reflow instead of 100.
05Events & Event Delegation

Events make the DOM interactive. The browser fires events when things happen โ€” clicks, key presses, form submissions, page load, etc.

// ADDING EVENT LISTENERS
const btn = document.querySelector('#myBtn');
btn.addEventListener('click', function(event) {
  console.log('Clicked!');
  console.log('Type:', event.type);         // 'click'
  console.log('Target:', event.target);      // the element clicked
  console.log('CurrentTarget:', event.currentTarget); // element with listener
});

// PREVENT DEFAULT BEHAVIOR
const link = document.querySelector('a');
link.addEventListener('click', function(e) {
  e.preventDefault();   // stops link navigation
  console.log('Link click intercepted!');
});

// STOP EVENT BUBBLING
child.addEventListener('click', function(e) {
  e.stopPropagation();  // parent won't see this click
});

// REMOVE LISTENER (must pass same function reference)
function handleClick(e) { console.log('hi'); }
btn.addEventListener('click', handleClick);
btn.removeEventListener('click', handleClick);
Event Delegation โ€” the most important pattern: Instead of adding 100 listeners to 100 list items, add ONE listener on the parent <ul>. When any <li> is clicked, check event.target to see which one it was. This is faster, uses less memory, and automatically works for dynamically added elements.
// Event delegation in action
const list = document.querySelector('ul');
list.addEventListener('click', function(e) {
  const li = e.target.closest('li');  // find the <li> ancestor
  if (!li) return;                       // click wasn't on a <li>
  console.log('Clicked item:', li.textContent);
});
event.target vs event.currentTarget: event.target = the element that was actually clicked (could be a child span inside a button). event.currentTarget = the element that has the addEventListener. In event delegation, these are different. Use .closest() to find the right ancestor.

Lo kar liya โ€” Key Points:

  • โœ… DOM = browser converts HTML into a live tree of JavaScript objects
  • โœ… getElementById for IDs, querySelector for CSS selectors, querySelectorAll for multiple
  • โœ… querySelectorAll returns NodeList โ€” NOT an Array, use [...nodes] to convert
  • โœ… textContent for safe text, innerHTML only for trusted content (XSS danger!)
  • โœ… classList.add/remove/toggle is the modern way to handle CSS classes
  • โœ… Event delegation = ONE listener on parent + event.target.closest() to find the child
  • โœ… event.target โ‰  event.currentTarget in delegation โ€” know the difference!
Course Search
Search across all chapters & stages
๐Ÿ“–

Search the course

Type any topic โ€” branching, stash, rebase, hooks โ€” and jump straight to that chapter.

merge branchesgit stashundo commitrebase