Refresher Reminder

🌐

JavaScript in the Browser

Everything specific to running JS inside web pages

🪟 Window 📄 Document 🌳 DOM APIs 📦 Script Loading 🎯 Events 📡 AJAX
📚 General JavaScript refresher content lives in Refresher and stays exactly the same.

The window Global

Each browser tab has a top-level global object: window. It exposes:

🪟 Window Object Properties

🌍 Environment
location navigator history screen
⏱️ Timing
setTimeout setInterval requestAnimationFrame
📄 DOM Root
document
💬 Dialogs / Console
alert 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. Key properties:

📄 Document Quick Reference

📍 Element Shortcuts
document.documentElement → <html> element
document.head → <head> element
document.body → <body> element
ℹ️ Info Properties
document.title (read/write)
document.URL
document.referrer
🔄 Ready State
document.readyState
loading interactive complete
// Change the page title (watch the browser title bar!)
document.title = "My Custom Title!";
console.log("Title set to:", document.title);

// Create visible content
document.body.innerHTML = `
  <h1>Hello from JavaScript!</h1>
  <p>This content was created dynamically.</p>
  <div class="demo-box">
    <strong>Document Info:</strong>
    <ul>
      <li>Ready State: ${document.readyState}</li>
      <li>Character Set: ${document.characterSet}</li>
      <li>Content Type: ${document.contentType}</li>
    </ul>
  </div>
`;

console.log("Page content updated!");
console.log("Ready state:", document.readyState);

💡 Tip: Watch the browser preview panel — the title bar will update when you change document.title!

The DOM Tree

The DOM (Document Object Model) is a tree of nodes.

🌳 DOM Tree Structure

📦 Element nodes 📝 Text nodes 💬 Comment nodes 📎 Attribute nodes
⬆️ Parent Access
node.parentNode
Every node may have a parent
⬇️ Children Access
children (elements only)
childNodes (all node types)
↔️ Siblings
nextElementSibling
previousElementSibling

DOM Inheritance Hierarchy

Accurate core DOM type relationships (simplified). Hollow triangle points to the parent.

  • Node
    • 📄 Document
    • 📄 DocumentType
    • 📄 DocumentFragment
    • Element
      • HTMLElement
        • 📄 HTMLDivElement
        • 📄 HTMLButtonElement
        • 📄 HTMLInputElement
        • 📄 HTMLSpanElement
        • ⋯ and many more
      • SVGElement
        • 📄 SVGCircleElement
        • 📄 SVGPathElement
        • ⋯ more
    • CharacterData
      • 📄 Text
      • 📄 Comment
      • 📄 CDATASection
    • 📄 ProcessingInstruction
📎 Attr Not a Node (since DOM4)

Selecting Nodes & Elements

🎯 Selection Methods

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 NodeList
Snapshot at query time. DOM changes don't affect it.
🔄 Live HTMLCollection
Auto-updates when DOM changes. Can cause issues in loops!
🚀 Direct access: document.body, document.head, document.documentElement
// First, create some demo HTML
document.body.innerHTML = `
  <div id="demo-main" class="demo-box">
    <h2>Main Container</h2>
    <button class="demo-btn primary">Primary Button</button>
    <button class="demo-btn">Secondary</button>
    <p class="demo-text">Some text here</p>
  </div>
`;

// Get element by ID
const main = document.querySelector("#demo-main");
console.log("Found element:", main.tagName);

// Get all buttons
const buttons = document.querySelectorAll(".demo-btn");
console.log("Number of buttons:", buttons.length);

// Loop through buttons
buttons.forEach((btn, i) => {
  console.log("Button " + i + ":", btn.textContent);
  btn.setAttribute("style", "border: 3px solid lime;");
});

// Get first primary button
const primary = document.querySelector(".demo-btn.primary");
if (primary) {
  primary.setAttribute("style", "background: #4CAF50; color: white;");
  console.log("Primary button highlighted!");
}

Content & Attribute Manipulation

📝 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.

📎 Dataset API (data-* attributes)

HTML Attribute
data-user-id="123"
JavaScript Access
element.dataset.userId
READ
el.dataset.userId
WRITE
el.dataset.key = "val"
DELETE
delete el.dataset.key
⚠️ Use dataset NOT data: el.dataset.ok ✅ vs el.data.ok
// Create demo elements
document.body.innerHTML = `
  <div id="panel" class="demo-box">
    <p>Original content</p>
  </div>
  <input type="text" id="email" placeholder="Enter email">
`;

const panel = document.querySelector("#panel");

// textContent - treats HTML as literal text
panel.textContent = "<span>This shows as TEXT</span>";
console.log("1. textContent set (HTML escaped)");

// After 1s, use innerHTML
setTimeout(() => {
  panel.innerHTML = "<strong style='color:#4CAF50'>Bold HTML!</strong>";
  console.log("2. innerHTML set (renders HTML)");
}, 1000);

// Work with input attributes
const input = document.querySelector("#email");
input.value = "user@example.com";
input.setAttribute("placeholder", "Email updated!");
console.log("Input value:", input.value);

// === Dataset API Examples ===
// CORRECT: use element.dataset (not element.data!)
panel.dataset.state = "active";       // creates data-state="active"
panel.dataset.userId = "12345";       // creates data-user-id="12345"  
panel.dataset.ok = "works!";          // creates data-ok="works!"
panel.dataset.myCustomValue = "abc";  // creates data-my-custom-value="abc"

console.log("dataset.state:", panel.dataset.state);
console.log("dataset.userId:", panel.dataset.userId);
console.log("dataset.ok:", panel.dataset.ok);

// Read via getAttribute too
console.log("getAttribute:", panel.getAttribute("data-ok"));

// Delete a data attribute
delete panel.dataset.ok;
console.log("After delete, ok:", panel.dataset.ok); // undefined

Creating & Inserting Elements

🛠️ Creating & Inserting Methods

CREATE
document.createElement(tag)
APPEND (end)
parent.appendChild(node)
parent.append(...nodes)
PREPEND (start)
parent.prepend(node)
INSERT HTML
insertAdjacentHTML(pos, html)
📍 insertAdjacentHTML positions:
"beforebegin" → before element
<div>
"afterbegin" → first child
...content...
"beforeend" → last child
</div>
"afterend" → after element

🚀 DocumentFragment - Batch Insertions

Every DOM modification triggers a reflow/repaint (expensive!)

❌ Without Fragment
Adding 100 items = 100 reflows!
for(i) { parent.appendChild(item) }
✅ With Fragment
Adding 100 items = 1 reflow!
frag = createDocumentFragment()
for(i) { frag.appendChild(item) }
parent.appendChild(frag)
💾 Lives in memory only 👻 Fragment disappears after insert 📊 Perfect for lists & tables

🎮 Interactive Playground - Try It!

Choose Method:
Generated Code:
container.prepend(newElement);
Live Preview:
⬆️ beforebegin zone (outside)
📦 CONTAINER (target element)
afterbegin / prepend
📦 Item 1 (original)
📦 Item 2 (original)
📦 Item 3 (original)
beforeend / append
⬇️ afterend zone (outside)
🔵 Original 🟢 New 🟠 Outside 🔷 Inside

📝 Quick Examples

// 1. Create element
const div = document.createElement("div");
div.textContent = "Hello!";
div.className = "my-class";

// 2. Insert methods
const container = document.body;

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!

console.log("Elements created and inserted!");

Traversal & Relationships

🧭 DOM Navigation Properties

PARENT
↑ parentNode children ↓
← previous
firstElementChild
Child 1
children[1]
Child 2
lastElementChild
Child 3
next →
previousElementSibling
nextElementSibling
CHILDREN (elements only)
element.children
CHILD NODES (all types)
element.childNodes
PARENT
node.parentNode
SIBLINGS
nextElementSibling
previousElementSibling
Element Properties
Returns only element nodes (tags)
Node Properties
Includes text, comments, etc.
// Create article container
document.body.innerHTML = `
  <div id="articles">
    <article class="demo-box">Article 1</article>
    <article class="demo-box">Article 2</article>
    <article class="demo-box">Article 3</article>
  </div>
`;

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 element child
const first = container.firstElementChild;
const last = container.lastElementChild;
first.setAttribute("style", "background: #4CAF50; color: white; padding: 10px;");
console.log("First child:", first.textContent);

last.setAttribute("style", "background: #2196F3; color: white; padding: 10px;");
console.log("Last child:", last.textContent);

// Sibling navigation
const second = first.nextElementSibling;
second.setAttribute("style", "background: #FF9800; color: white; padding: 10px;");
console.log("Second (via nextElementSibling):", second.textContent);

// Parent access
console.log("Parent tag:", second.parentNode.tagName);
console.log("Parent id:", second.parentNode.id);

Script Loading Strategies

Ways to include JavaScript:

🎬 Choose a Loading Strategy:

<script src="app.js"></script>
Timeline Visualization
HTML
Script
Events
DCL
load
📍 When do browser events fire?
DOMContentLoaded
HTML fully parsed, DOM ready
window.load
Everything loaded (images, etc.)
⏱️ With blocking: DOMContentLoaded waits for script to finish
HTML Parsing Blocked Download Execute
🛑 Blocking Script (Default)

How it works: HTML parsing stops, script downloads and executes, then parsing resumes.

  • ❌ Blocks page rendering
  • ❌ Slows down page load
  • ✅ Scripts execute in order
  • ⚠️ Use only when script must run before DOM
Best for: Critical scripts that must modify the page before it renders (rare).

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!" — a click, a keypress, the page loading, etc.

👆
User Action
📡
Browser fires Event
Your Code Runs

📝 How to Listen for Events

❌ Inline (Don't Do This)
<button onclick="alert('Hi')">Click</button>
  • Mixes HTML with JavaScript
  • Only ONE handler per event
  • Hard to debug & maintain
✅ addEventListener (Use This!)
element.addEventListener("click", myFunction)
  • Clean separation of HTML/JS
  • Add multiple handlers
  • Can remove listeners later
🔴 Live Demo - Try It!
💡 Key Syntax: element.addEventListener("eventType", callbackFunction)

AJAX with XMLHttpRequest (XHR)

XHR predates fetch; still useful for progress events & legacy code. Prefer async; synchronous XHR blocks the UI thread.

📡 XHR Lifecycle - 5 Steps

STEP 1
Create
new XMLHttpRequest()
STEP 2
Configure
xhr.open(method, url)
STEP 3
Set Options
xhr.responseType
STEP 4
Add Listeners
xhr.addEventListener()
STEP 5
Send
xhr.send()
🎯 XHR Events
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()

JSON Data
xhr.setRequestHeader("Content-Type", "application/json")
Form Data
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
Authorization
xhr.setRequestHeader("Authorization", "Bearer token123")
Custom Header
xhr.setRequestHeader("X-Custom-Header", "value")
📤 Sending Data with send()
📭 No Data (GET requests)
xhr.send();
xhr.send(null);
📦 JSON Data (POST/PUT)
const data = { name: "John", age: 25 };
xhr.send(JSON.stringify(data));
📝 FormData (File uploads)
const formData = new FormData();
formData.append("file", fileInput.files[0]);
xhr.send(formData);
🔗 URL Encoded
xhr.send("name=John&age=25");
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

📝 Full Async Example

// STEP 1: Create XHR object
const xhr = new XMLHttpRequest();

// STEP 2: Configure request - open(method, url, async)
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1", true);

// STEP 3: Set response type
xhr.responseType = "json";

// STEP 4: Add event listeners
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!"));

// STEP 5: Send the request
xhr.send();

⚠️ Sync Example (Not Recommended)

// ⚠️ 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 Use Deprecated — 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!
🔗 Sequential: When each request depends on the previous
Promise.all: When requests are independent (fastest!)
🛡️ Promise.allSettled: When you want results even if some fail
🏃 Promise.race: When you need only the fastest response