đ JavaScript in the Browser
When JavaScript runs in a browser, it gets access to powerful APIs for manipulating the web page. The DOM (Document Object Model) is the bridge between your JavaScript code and the HTML on screen.
In Module 2, you mastered modern JavaScript syntax â spread, destructuring, Promises, and async/await. Now it's time to use those skills in the browser. This module covers everything from the global window object to selecting and creating DOM elements, handling user events, and fetching data from servers â the complete toolkit for building interactive web pages.
đ¯ By the end of this module, you will:
- Understand the
windowanddocumentobjects - Navigate the DOM tree and its inheritance hierarchy
- Select, create, modify, traverse, and remove DOM elements
- Handle events: types, flow (capture/bubble), delegation
- Choose the right script loading strategy (
defer,async) - Fetch data with
XMLHttpRequestandfetch() - Understand the evolution from Callback Hell to
Promise.all
Want to practice? Try our Web Playground!
Write HTML, CSS & JavaScript with live preview â no signup required.
đĒ The window Global
Each browser tab has a top-level global object: window. It exposes:
location navigator history screensetTimeout setInterval requestAnimationFramedocumentalert prompt confirm consoleđĄ Tip: You can omit window. for global functions: alert("Hi") equals window.alert("Hi")
console.log("Current URL:", window.location.href);
console.log("User Agent:", window.navigator.userAgent);
console.log("Screen size:", window.screen.width, "x", window.screen.height);
// Timing example
window.setTimeout(() => console.log("Timer fired after 500ms"), 500);
// Another way (window. is optional)
setTimeout(() => console.log("Same timer syntax"), 600);đ The document Object
document represents the loaded HTML page. It is your entry point for reading and changing anything on screen â from the page title to individual elements. Key properties:
document.documentElement â <html>document.head â <head>document.body â <body>
document.title (read/write)document.URLdocument.referrer
document.readyState
đĄ Try it now: Open your browser DevTools (F12) and type document.title = "Hello!" â watch the tab title change in real-time!
// Change the page title (watch the browser tab!)
document.title = "My Custom Title!";
console.log("Title:", document.title);
// Access key elements
console.log("URL:", document.URL);
console.log("Ready state:", document.readyState);
console.log("Character set:", document.characterSet);
// Direct element shortcuts
const html = document.documentElement; // <html>
const head = document.head; // <head>
const body = document.body; // <body>
console.log("HTML lang:", html.lang);
console.log("Body children:", body.children.length);đŗ The DOM Tree
The DOM (Document Object Model) is a tree of nodes. The browser parses your HTML and builds this tree. JavaScript can read and modify it in real-time.
HTML â DOM Tree
<!-- HTML Source -->
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1>Hello</h1>
<p class="intro">Welcome!</p>
</body>
</html>
Node Types
Key Relationships
node.parentNode
children (elements only)childNodes (all node types)
nextElementSiblingpreviousElementSibling
đī¸ DOM Inheritance Hierarchy
Accurate core DOM type relationships (simplified). Click nodes to expand/collapse branches.
Understanding DOM inheritance explains why methods work the way they do. Every <div>, <button>, or <input> is an instance of a specific class (like HTMLDivElement) that inherits from HTMLElement â Element â Node. That's why you can call .addEventListener() on any element â it's inherited from EventTarget, the root of the chain.
-
âŧ Node
- đ Document
- đ DocumentType
- đ DocumentFragment
-
âŧ Element
-
âŧ HTMLElement
- đ HTMLDivElement
- đ HTMLButtonElement
- đ HTMLInputElement
- đ HTMLSpanElement
- ⯠and many more
-
âļ SVGElement
- đ SVGCircleElement
- đ SVGPathElement
- ⯠more
-
âŧ HTMLElement
-
âŧ CharacterData
- đ Text
- đ Comment
- đ CDATASection
- đ ProcessingInstruction
đĄ Practical takeaway: You don't need to memorize this tree. Just know that specific elements (like HTMLInputElement) inherit everything from their ancestors â that's why .value exists on inputs but not divs, while .textContent and .addEventListener() work on everything.
đ¯ Selecting Elements
Before you can read, modify, or remove an element, you need to find it in the DOM. JavaScript provides several methods â from fast ID lookups to powerful CSS-selector-based queries. Choose the right one depending on what you need:
document.getElementById(id)
Single element by ID
⥠Fastest
document.querySelector(css)
First match (any CSS selector)
đ¨ Flexible
document.querySelectorAll(css)
All matches â static NodeList
đ Static
getElementsByClassName(name)
By class â live HTMLCollection
đ Live
getElementsByTagName(tag)
By tag â live HTMLCollection
đ Live
Static vs Live Collections
đ Static NodeList
Snapshot at query time. DOM changes don't affect it. Returned by querySelectorAll.
đ Live HTMLCollection
Auto-updates when DOM changes. Can cause issues in loops! Returned by getElementsBy*.
âļ đ Deep Dive: Static vs Live â Why It Matters
đ The Core Concept
Think of it like a photo vs a mirror. A static collection is a photo â it captures the state at the moment you took it. A live collection is a mirror â it always reflects the current state of the DOM.
.product.product element to the DOM// === SETUP: Page has 3 .product divs ===
// đ¸ Static snapshot (photo)
const staticList = document.querySelectorAll('.product');
// đĒ Live mirror
const liveList = document.getElementsByClassName('product');
console.log(staticList.length); // 3
console.log(liveList.length); // 3
// === MUTATION: Add a 4th product ===
const newProduct = document.createElement('div');
newProduct.className = 'product';
document.body.appendChild(newProduct);
// === CHECK AGAIN ===
console.log(staticList.length); // 3 â still! (photo doesn't change)
console.log(liveList.length); // 4 â updated! (mirror reflects reality)â ī¸ The Dangerous Loop Bug
Live collections can cause infinite loops when you modify the DOM inside a loop, because the collection keeps updating:
// â DANGEROUS â Infinite loop!
const items = document.getElementsByClassName('old');
for (let i = 0; i < items.length; i++) {
items[i].className = 'new';
// Problem: items.length SHRINKS as items lose 'old' class!
// i=0 â changes first item â length drops from 3 to 2
// i=1 â skips the SECOND item (now at index 0)!
// Items get skipped, or loop runs forever if you ADD the class
}
// â
SAFE â Static doesn't change during loop
const items2 = document.querySelectorAll('.old');
items2.forEach(item => {
item.className = 'new'; // Safe! items2 is frozen
});
// â
ALSO SAFE â Convert live to static array first
const items3 = [...document.getElementsByClassName('old')];
items3.forEach(item => {
item.className = 'new'; // Safe! Array is a copy
});đ Static NodeList |
đ Live HTMLCollection |
|
|---|---|---|
| Method | querySelectorAll() |
getElementsBy*() |
| Auto-updates? | â No â frozen snapshot | â Yes â always current |
| forEach? | â Native support | â Must convert: [...list] |
| Loop-safe? | â Always safe | â ī¸ Risky if DOM mutates |
| Performance | Slightly slower (builds snapshot) | Faster (no copy needed) |
| Use when | Iterating, modifying, storing | Checking count, quick reads |
đ Rule of thumb: Always use querySelectorAll() unless you specifically need live updates. It's safer, more powerful (any CSS selector), and supports forEach natively.
đ Direct access: document.body, document.head, document.documentElement â no selector needed!
// Modern selectors (recommended â
)
const el = document.querySelector('.my-class'); // first match
const all = document.querySelectorAll('.my-class'); // static NodeList
// By ID (fastest)
const header = document.getElementById('header');
// CSS selectors work!
document.querySelector('nav > ul > li:first-child');
document.querySelectorAll('[data-active="true"]');
document.querySelector('#form input[type="email"]');
// Checking elements
el.matches('.active'); // does it match this selector?
el.closest('.container'); // nearest ancestor matching selector
// â ī¸ Live vs Static
const live = document.getElementsByClassName('item'); // đ Live
const snap = document.querySelectorAll('.item'); // đ Static
document.body.appendChild(document.createElement('div')).className = 'item';
console.log(live.length); // increased! (live auto-updates)
console.log(snap.length); // same! (snapshot doesn't change)đ Content & Attribute Manipulation
Once you've selected an element, you can change its content, attributes, styles, and classes. JavaScript offers multiple ways to update what the user sees â from safe text replacement to full HTML injection. Understanding the differences is critical for both functionality and security.
Text/Content APIs Comparison
textContent
â
Safe
⥠Fast
Raw text only. No layout cost. HTML is escaped as text.
innerText
â ī¸ Reflow
Respects CSS visibility. Triggers layout/reflow reads.
innerHTML
â ī¸ XSS Risk
Parses HTML. Never use with untrusted input!
đ Security: Sanitize external HTML (e.g. DOMPurify) or build nodes manually to prevent XSS attacks.
đ Dataset API (data-* attributes)
data-user-id="123"
element.dataset.userId
el.dataset.userIdel.dataset.key = "val"delete el.dataset.keyâ ī¸ Important: Use dataset, not data: el.dataset.ok â
vs el.data.ok â
const panel = document.querySelector("#panel");
// textContent - treats HTML as literal text (safe â
)
panel.textContent = "<span>Shows as TEXT</span>";
// innerHTML - renders HTML (XSS risk â ī¸)
panel.innerHTML = "<strong style='color:green'>Bold HTML!</strong>";
// Attributes
const input = document.querySelector("#email");
input.value = "user@example.com";
input.setAttribute("placeholder", "Enter email");
// Styles
el.style.color = 'red';
el.style.backgroundColor = 'blue';
// Better: toggle classes (let CSS handle styles)
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('open');
el.classList.contains('active'); // true/false
// Dataset API
panel.dataset.state = "active"; // creates data-state="active"
panel.dataset.userId = "12345"; // creates data-user-id="12345"
console.log(panel.dataset.state); // "active"
delete panel.dataset.state; // removes data-stateđ ī¸ Creating & Inserting Elements
Dynamic web pages don't just display static HTML â they build new elements on the fly. Whether you're rendering a list of search results, adding a notification banner, or creating a modal dialog, you need to create DOM nodes in JavaScript and insert them at the right place in the tree.
document.createElement(tag)parent.appendChild(node)parent.append(...nodes)parent.prepend(node)insertAdjacentHTML(pos, html)đ insertAdjacentHTML positions
đ DocumentFragment â Batch Insertions
Every DOM modification triggers a reflow/repaint (expensive!)
â Without Fragment
Adding 100 items = 100 reflows!
const parent = document.body;
for (let i = 0; i < 100; i++) {
const item = document.createElement('div');
item.textContent = 'Item ' + i;
parent.appendChild(item); // reflow each time!
}â With Fragment
Adding 100 items = 1 reflow!
const parent = document.body;
const frag = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const item = document.createElement('div');
item.textContent = 'Item ' + i;
frag.appendChild(item);
}
parent.appendChild(frag); // ONE reflow!// 1. Create element
const div = document.createElement("div");
div.textContent = "Hello!";
div.className = "my-class";
// 2. Insert methods
container.prepend(div); // First child
container.append(div); // Last child
container.appendChild(div); // Same as append
// 3. insertAdjacentHTML positions
container.insertAdjacentHTML("beforebegin", "<p>Before</p>");
container.insertAdjacentHTML("afterbegin", "<p>First inside</p>");
container.insertAdjacentHTML("beforeend", "<p>Last inside</p>");
container.insertAdjacentHTML("afterend", "<p>After</p>");
// 4. DocumentFragment for batch inserts
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const item = document.createElement("div");
item.textContent = "Item " + i;
fragment.appendChild(item); // No reflow yet!
}
container.appendChild(fragment); // ONE reflow!
// 5. Remove & Clone
div.remove(); // Remove from DOM
const clone = div.cloneNode(true); // Deep cloneđŽ Interactive Playground â Try It!
const newEl = document.createElement('div');
newEl.textContent = "New Item â";
container.prepend(newEl);
đ§ Traversal & Relationships
Navigate the DOM tree using relationship properties:
element.children
element.childNodes
node.parentNode
nextElementSiblingpreviousElementSibling
Element Properties
Returns only element nodes (tags). children, firstElementChild, nextElementSiblingâĻ
Node Properties
Includes text, comments, etc. childNodes, firstChild, nextSiblingâĻ
const container = document.querySelector("#articles");
// Children info
console.log("Element children:", container.children.length);
console.log("All child nodes:", container.childNodes.length);
// First and last
const first = container.firstElementChild;
const last = container.lastElementChild;
console.log("First:", first.textContent);
console.log("Last:", last.textContent);
// Sibling navigation
const second = first.nextElementSibling;
console.log("Second:", second.textContent);
// Go back up
console.log("Parent:", second.parentNode.tagName);
// Find nearest ancestor
const btn = document.querySelector('.delete-btn');
const card = btn.closest('.card-wrapper'); // nearest ancestorđ Script Loading Strategies
Where and how you load your <script> tags affects page performance:
đ <script> (default)
Blocks HTML parsing. Browser stops, downloads, runs, then continues.
- â Blocks page rendering
- â Slows down page load
- â Scripts execute in order
âŗ <script defer> â
Downloads in parallel, runs after parsing. Before DOMContentLoaded.
- â HTML parsing never blocked
- â Scripts execute in order
- â Full DOM is available
⥠<script async>
Downloads in parallel, runs immediately. Order not guaranteed.
- â Downloads in parallel
- â ī¸ Order NOT guaranteed
- â ī¸ DOM may be incomplete
đŦ Timeline Visualization
đ Blocking: HTML parsing stops at the script tag. Browser downloads and executes, then resumes. DOMContentLoaded waits for the script.
đ When do browser events fire?
DOMContentLoaded
HTML fully parsed, DOM ready. Images/stylesheets may still be loading.
window.load
Everything loaded (images, styles, iframes). Full page is ready.
đĻ Note: type="module" scripts behave exactly like defer by default!
<!-- In <head> with defer â RECOMMENDED â
-->
<head>
<script src="app.js" defer></script>
</head>
<!-- async â for independent scripts -->
<script src="analytics.js" async></script>
<!-- DOMContentLoaded â wait for DOM to be ready -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const app = document.getElementById('app');
});
</script>
<!-- module scripts = defer by default -->
<script type="module" src="app.mjs"></script>⥠Events: How JavaScript Listens to the Browser
Events are signals the browser sends when something happens â a click, key press, page load, etc. Your code can "listen" and respond.
đ¯ What is an Event?
An event is the browser saying: "Hey, something just happened!"
đ How to Listen for Events
â Inline (Don't Do This)
<button onclick="alert('Hi')">
- Mixes HTML with JS
- Only ONE handler per event
- Hard to debug
â addEventListener
el.addEventListener("click", fn)
- Clean separation
- Multiple handlers
- Can remove listeners
đĄ Key Syntax: element.addEventListener("eventType", callbackFunction)
đ Common Event Types
đąī¸ Mouse Events
â¨ī¸ Keyboard Events
đ Form Events
đ Page Events
đĻ The Event Object
Every handler receives an event object with useful info:
e.targetElement that triggered the evente.currentTargetElement the listener is one.typeEvent type ("click", etc.)e.preventDefault()Stop default behaviore.stopPropagation()Stop bubbling upe.keyWhich key was pressede.clientX / clientYMouse position (viewport)button.addEventListener('click', (e) => {
console.log(e.type); // "click"
console.log(e.target); // the <button> element
console.log(e.clientX); // mouse X position
console.log(e.timeStamp); // when event occurred (ms)
e.preventDefault(); // stop default action
e.stopPropagation(); // stop event bubbling
});đ Event Flow: Capturing & Bubbling
When you click an element, the event travels in 3 phases:
From window down to target
At the clicked element
From target up to window
// BUBBLING (default) - Events go UP â
outer.addEventListener('click', () => console.log('OUTER'));
middle.addEventListener('click', () => console.log('MIDDLE'));
inner.addEventListener('click', () => console.log('INNER'));
// Click inner â INNER, MIDDLE, OUTER
// CAPTURING - Events go DOWN â (third param = true)
outer.addEventListener('click', () => console.log('OUTER'), true);
// Click inner â OUTER, MIDDLE, INNER
// Stop propagation
inner.addEventListener('click', (e) => {
e.stopPropagation(); // parents won't receive it
console.log('INNER - STOPPED');
});đ¤ Why would you use Capture Phase?
Capture lets you intercept events before they reach the target. Think of it like a security guard checking visitors before they enter a building:
Parent can validate or cancel events before children handle them
Track all clicks from the top before they reach their targets
Modal dialogs can capture focus events to keep focus inside
đĄ 99% of the time you'll use bubbling (default). Capture is for special cases where you need to act first.
đ´ Click the Inner Box â Watch the Flow!
stopPropagation() on INNER prevents MIDDLE and OUTER from receiving the event.
đĒ Event Delegation
Instead of adding listeners to many children, add ONE listener to the parent!
â Bad: Many Listeners
// 100 buttons = 100 listeners đ°
buttons.forEach(btn => {
btn.addEventListener("click", handle);
});â Good: Delegation
// 1 listener handles all! đ
container.addEventListener("click", e => {
if (e.target.matches("button")) {
handle(e.target);
}
});// Practical: Todo list with delegation
const list = document.querySelector('.todo-list');
// ONE listener handles ALL children
list.addEventListener('click', (e) => {
if (e.target.matches('.delete-btn')) {
e.target.closest('.todo-item').remove();
} else if (e.target.tagName === 'LI') {
e.target.classList.toggle('selected');
}
});
// New items work automatically!
function addItem(text) {
const li = document.createElement('li');
li.innerHTML = `${text} <button class="delete-btn">Ã</button>`;
list.appendChild(li);
// No new listener needed - parent already handles it!
}
// Key methods: e.target, e.target.matches(), e.target.closest()
// Remove & once
btn.removeEventListener('click', handler);
btn.addEventListener('click', fn, { once: true }); // fires onceđĄ AJAX: From XHR to Modern Fetch
AJAX lets you request data from a server without reloading the page. Let's trace the evolution from XHR to modern fetch().
đĄ XMLHttpRequest (XHR) â 5 Steps
new XMLHttpRequest()xhr.open(method, url)xhr.responseTypexhr.addEventListener()xhr.send()load Success
error Network fail
progress Downloading
loadend Finished
timeout Too slow
abort Cancelled
đ Setting Request Headers
Use setRequestHeader(name, value) after open() but before send()
xhr.setRequestHeader("Content-Type", "application/json")xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")xhr.setRequestHeader("Authorization", "Bearer token123")xhr.setRequestHeader("X-Custom-Header", "value")đ¤ Sending Data with send()
xhr.send() or xhr.send(null)xhr.send(JSON.stringify(data))xhr.send(new FormData(form))xhr.send("name=John&age=25")// Full Async XHR Example
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1", true);
xhr.responseType = "json";
xhr.addEventListener("load", () => {
if (xhr.status === 200) {
console.log("â
Success:", xhr.response.title);
} else {
console.log("â Error:", xhr.status);
}
});
xhr.addEventListener("error", () => console.log("â Network error!"));
xhr.send();â Async (Recommended)
xhr.open("GET", url, true)
- Non-blocking
- UI stays responsive
- Uses event listeners
â Sync (Avoid!)
xhr.open("GET", url, false)
- Blocks browser
- UI freezes
- Deprecated in main thread
⨠Modern fetch() API
// Basic GET request
const response = await fetch('https://api.example.com/users');
const users = await response.json();
// With error handling
async function fetchUsers() {
try {
const res = await fetch('/api/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
console.error('Fetch failed:', err.message);
}
}
// POST request (sending data)
await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Mehdi', role: 'teacher' })
});
// Other methods
await fetch('/api/users/1', { method: 'PUT', headers: {...}, body: ... });
await fetch('/api/users/1', { method: 'DELETE' });
// Practical: load data and update DOM
async function loadPosts() {
const container = document.getElementById('posts');
container.innerHTML = '<p>Loading...</p>';
try {
const res = await fetch('/api/posts');
const posts = await res.json();
container.innerHTML = posts
.map(p => `<article><h3>${p.title}</h3></article>`)
.join('');
} catch(e) {
container.innerHTML = '<p class="error">Failed to load</p>';
}
}Important: fetch() only rejects on network errors, NOT on HTTP errors (404, 500). Always check response.ok.
đ From Callback Hell to Modern Fetch
Compare how we fetch 10 numbers and sum them â from the worst pattern to the best. Click the buttons below to run each approach live and see the timing difference!
// đĢ XHR SYNCHRONOUS: NEVER USE THIS!
// The third parameter "false" makes it synchronous
// 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);
// ... (n3 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!// đ° CALLBACK HELL: 10 nested callbacks!
// 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!)// đ PROMISE CHAINS: Flat but still sequential!
// Using DummyJSON API â fetching product prices as random numbers
const getAPI = () =>
`https://dummyjson.com/products/${Math.ceil(Math.random()*100)}`;
const getRandomFromServer = () => {
return fetch(getAPI())
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.then(data => Math.round(data.price));
};
let n1, n2, n3, n4, n5, n6, n7, n8, n9, n10;
getRandomFromServer()
.then(n => { n1 = n; return getRandomFromServer(); })
.then(n => { n2 = n; return getRandomFromServer(); })
.then(n => { n3 = n; return getRandomFromServer(); })
.then(n => { n4 = n; return getRandomFromServer(); })
.then(n => { n5 = n; return getRandomFromServer(); })
.then(n => { n6 = n; return getRandomFromServer(); })
.then(n => { n7 = n; return getRandomFromServer(); })
.then(n => { n8 = n; return getRandomFromServer(); })
.then(n => { n9 = n; return getRandomFromServer(); })
.then(n => {
n10 = n;
const sum = n1+n2+n3+n4+n5+n6+n7+n8+n9+n10;
console.log("Sum:", sum);
})
.catch(err => console.error(err));
// âąī¸ Total time: ~2000ms (still sequential!)// ⨠ASYNC/AWAIT: Clean but SEQUENTIAL (slow for 10 requests!)
// Using DummyJSON API â fetching product prices as random numbers
const getAPI = () =>
`https://dummyjson.com/products/${Math.ceil(Math.random()*100)}`;
const getRandomFromServer = async () => {
const response = await fetch(getAPI());
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return Math.round(data.price);
};
(async () => {
// â ī¸ Each await WAITS for the previous one!
const n1 = await getRandomFromServer();
const n2 = await getRandomFromServer();
const n3 = await getRandomFromServer();
const n4 = await getRandomFromServer();
const n5 = await getRandomFromServer();
const n6 = await getRandomFromServer();
const n7 = await getRandomFromServer();
const n8 = await getRandomFromServer();
const n9 = await getRandomFromServer();
const n10 = await getRandomFromServer();
const sum = n1+n2+n3+n4+n5+n6+n7+n8+n9+n10;
console.log("Sum:", sum);
})();
// âąī¸ Total time: ~2000ms (10 sequential requests)// ⥠PROMISE.ALL: All 10 requests run at the SAME TIME!
// Using DummyJSON API â fetching product prices as random numbers
const getAPI = () =>
`https://dummyjson.com/products/${Math.ceil(Math.random()*100)}`;
const getRandomFromServer = async () => {
const response = await fetch(getAPI());
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return Math.round(data.price);
};
(async () => {
// đ All 10 requests start simultaneously!
const [n1, n2, n3, n4, n5, n6, n7, n8, n9, n10] = await Promise.all([
getRandomFromServer(),
getRandomFromServer(),
getRandomFromServer(),
getRandomFromServer(),
getRandomFromServer(),
getRandomFromServer(),
getRandomFromServer(),
getRandomFromServer(),
getRandomFromServer(),
getRandomFromServer()
]);
const sum = n1+n2+n3+n4+n5+n6+n7+n8+n9+n10;
console.log("Sum:", sum);
})();
// âąī¸ Total time: ~200ms (all 10 in parallel!)
// đ That's 10Ã FASTER than sequential!Promise Combinators Summary
When each request depends on the previous
Independent requests (fastest!)
Results even if some fail
Only the fastest response
đ Summary
This module covered the entire browser-side JavaScript toolkit. You now know how the window and document objects work, how to navigate and manipulate the DOM tree, how to select, create, and insert elements efficiently, how events flow through the page (capture â target â bubble), and how to fetch data from servers with XHR and the modern Fetch API. These are the skills that turn static HTML into living, interactive web applications. In the next module, you'll leave the browser and enter the world of Node.js â JavaScript on the server.
đĒ Window
Global object: environment, timing, dialogs. window. is optional.
đ Document
Loaded HTML page. Shortcuts: .body, .head, .title.
đŗ DOM Tree
Tree of nodes. Element, text, comment, attribute types.
đī¸ Inheritance
Node â Element â HTMLElement â specific elements.
đ¯ Selectors
querySelector / querySelectorAll. Static vs Live.
đ Manipulation
textContent (safe), innerHTML (XSS risk), Dataset API.
đ ī¸ Creation
createElement, append, insertAdjacentHTML, Fragment.
đ§ Traversal
children, parentNode, nextElementSibling, closest.
đ Scripts
defer for most. async for independent. module = defer.
⥠Events
Capture â Target â Bubble. addEventListener, delegation.
đĄ XHR
5-step lifecycle. Async recommended. Legacy but useful.
đ Fetch
Modern AJAX. async/await + Promise.all for parallel.