// 1 listener handles all! đ
container.addEventListener("click", e => {
if (e.target.matches("button")) {
handle(e.target);
}
});
â Less memory used
â Works on dynamic elements
â Easier cleanup
đ´ Add Items - They Work Without New Listeners!
Item 1
Item 2
Click items or à to delete (using delegation!)
// Get the parent container (NOT individual items)const list = document.getElementById('delegation-list');
// ONE listener on the parent handles ALL children!
list.addEventListener('click', (e) => {
// Check what was actually clicked using e.targetif (e.target.matches('.delete-btn')) {
// Delete button was clicked
e.target.parentElement.remove();
console.log('Deleted!');
}
else if (e.target.tagName === 'LI') {
// List item was clicked
console.log('Selected:', e.target.textContent);
}
});
// Adding new items - they automatically work!functionaddItem() {
const li = document.createElement('li');
li.innerHTML = 'New Item <button class="delete-btn">Ã</button>';
list.appendChild(li);
// No need to add new listener! Parent already handles it.
}
// Key methods for delegation:// e.target - the actual clicked element// e.target.matches('.class') - check if element matches selector// e.target.closest('.class') - find closest ancestor matching selector
AJAX with XMLHttpRequest (XHR)
XHR predates fetch; still useful for progress events & legacy code. Prefer async; synchronous XHR blocks the UI thread.
// â ī¸ SYNCHRONOUS - Browser freezes until complete!
const xhr = new XMLHttpRequest();
// async = FALSE makes it synchronous (blocking)
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1", false);
// Code WAITS here - no event listeners needed
xhr.send();
// Runs only after response arrives
if (xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
console.log("đĻ Title:", data.title);
}
// â UI was frozen the entire time!
console.log("đĄ Always use async (true) instead!");
Migration tip:Modern code usually uses fetch() + Promises instead of XHR for simpler APIs.
From Callback Hell to Modern Fetch
Sum of 10 numbers:â=ââąâ
đ Click a button to compare approaches...
đĢ Never UseDeprecated â blocks the entire browser!
â ī¸
XHR Sync blocks the entire UI!The browser freezes completely â user cannot scroll, click, or interact with anything until the request completes. This is deprecated and should never be used in production!
// đĢ XHR SYNCHRONOUS: NEVER USE THIS!
// The third parameter "false" makes it synchronous
// đ Note: We write 10 explicit calls for clarity
// Using DummyJSON API - fetching product prices as random numbers
const getAPI = () => `https://dummyjson.com/products/${Math.ceil(Math.random()*100)}`;
const xhr1 = new XMLHttpRequest();
xhr1.open("GET", getAPI(), false); xhr1.send();
const n1 = Math.round(JSON.parse(xhr1.responseText).price);
const xhr2 = new XMLHttpRequest();
xhr2.open("GET", getAPI(), false); xhr2.send();
const n2 = Math.round(JSON.parse(xhr2.responseText).price);
const xhr3 = new XMLHttpRequest();
xhr3.open("GET", getAPI(), false); xhr3.send();
const n3 = Math.round(JSON.parse(xhr3.responseText).price);
// ... (n4 through n9 same pattern) ...
const xhr10 = new XMLHttpRequest();
xhr10.open("GET", getAPI(), false); xhr10.send();
const n10 = Math.round(JSON.parse(xhr10.responseText).price);
const sum = n1 + n2 + n3 + n4 + n5 + n6 + n7 + n8 + n9 + n10;
console.log("Sum:", sum);
// đĢ UI was frozen the whole time!
â AvoidDeep nesting, repeated error handling, hard to maintain
// đ° CALLBACK HELL: 10 nested callbacks!
// đ Note: We show all 10 levels explicitly for clarity
// Using DummyJSON API - fetching product prices as random numbers
const getAPI = () => `https://dummyjson.com/products/${Math.ceil(Math.random()*100)}`;
const getRandomAsync = (onSuccess, onFail) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", getAPI(), true);
xhr.onload = () => xhr.status === 200
? onSuccess(Math.round(JSON.parse(xhr.response).price))
: onFail(xhr.status);
xhr.onerror = () => onFail("Network error");
xhr.send();
};
// The pyramid of doom đą
getRandomAsync(n1 => {
getRandomAsync(n2 => {
getRandomAsync(n3 => {
getRandomAsync(n4 => {
getRandomAsync(n5 => {
getRandomAsync(n6 => {
getRandomAsync(n7 => {
getRandomAsync(n8 => {
getRandomAsync(n9 => {
getRandomAsync(n10 => {
const sum = n1+n2+n3+n4+n5+n6+n7+n8+n9+n10;
console.log("Sum:", sum);
}, e => console.error(e));
}, e => console.error(e));
}, e => console.error(e));
}, e => console.error(e));
}, e => console.error(e));
}, e => console.error(e));
}, e => console.error(e));
}, e => console.error(e));
}, e => console.error(e));
}, e => console.error(e));
// âąī¸ Total time: ~2000ms (sequential!)
â BetterFlat chain, single error handler, but still sequential