Skip to content

DOM, Events and Async

DOM

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM Example</title>
<style>
.main-content {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.description {
color: #666;
}
.button-group {
margin: 20px 0;
}
</style>
</head>
<body>
<div id="container" class="main-content">
<h1>Welcome to my page</h1>
<p class="description">This is a sample DOM structure</p>
<div class="button-group">
<button id="myButton">Click me!</button>
</div>
<img src="sample.jpg" alt="Sample image" data-custom="value">
</div>
<script>
// DOM manipulation examples
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
alert('Button clicked!');
});
</script>
</body>
</html>

DOM (Document Object Model) is like a tree structure that represents your webpage. It allows JavaScript to:

  • Read webpage content
  • Change elements and text
  • Update styles
  • React to user actions
// Examples
document.getElementById('myButton') // Find element
document.querySelector('.myClass') // Find first matching element
element.innerHTML = 'New text' // Change content
element.style.color = 'red' // Change style
// # is used to select id
// . is used to select class

DOM Tree

Window
Document
HTML
┌────────┴────────┐
▼ ▼
HEAD BODY
┌───────┼────────┐ ┌────┴────┐
▼ ▼ ▼ ▼ ▼
meta style title div div
[charset="UTF-8"] [id] [class]
meta [class] │
[name="viewport"] │ ▼
link ▼ h1,img,p
[rel="stylesheet"] p,a [data-attr]
[href, class] [src, alt]

DOM Methods

Common DOM manipulation methods include:

// Finding Elements
document.getElementById('myId') // Returns single element by ID
document.getElementsByClassName('myClass') // Returns HTMLCollection of elements
document.getElementsByTagName('div') // Returns HTMLCollection of elements
document.querySelector('.myClass') // Returns first matching element
document.querySelectorAll('div.myClass') // Returns NodeList of all matching elements
// Creating & Modifying Elements
document.createElement('div') // Creates new element
element.appendChild(childElement) // Adds child element
element.removeChild(childElement) // Removes child element
element.replaceChild(newChild, oldChild) // Replaces child element
element.cloneNode(true) // Clones element (true = deep clone)
// Modifying Content
element.textContent = 'New text' // Updates text (safer)
element.innerHTML = '<span>New HTML</span>' // Updates HTML content
element.setAttribute('class', 'newClass') // Sets attribute
element.getAttribute('class') // Gets attribute
element.removeAttribute('class') // Removes attribute
// Styling
element.style.backgroundColor = 'red' // Direct style modification
element.classList.add('newClass') // Adds class
element.classList.remove('oldClass') // Removes class

NodeList

A NodeList is a collection of nodes returned by methods like querySelectorAll() or properties like childNodes. It’s similar to an array but has some important differences.

// Getting a NodeList
const paragraphs = document.querySelectorAll('p');
const childNodes = element.childNodes;
// Accessing Elements
const firstParagraph = paragraphs[0]; // Using index
const length = paragraphs.length; // Getting length
paragraphs.item(1); // Using item() method
// Converting to Array
const array = Array.from(paragraphs); // Modern way
const arrayAlt = [...paragraphs]; // Spread operator
const oldArray = Array.prototype.slice.call(paragraphs); // Old way
// Iterating
// 1. forEach method
paragraphs.forEach(paragraph => {
paragraph.classList.add('highlight');
});
// 2. for...of loop
for (const paragraph of paragraphs) {
paragraph.style.color = 'blue';
}
// 3. Traditional for loop
for (let i = 0; i < paragraphs.length; i++) {
paragraphs[i].textContent = `Paragraph ${i + 1}`;
}

Events

Events are actions or occurrences that happen in a web browser, such as user interactions or system updates. JavaScript can detect and respond to these events.

Event Handling

// 1. Adding Event Listeners
element.addEventListener('click', function(event) {
console.log('Clicked!');
});
// 2. Removing Event Listeners
const handler = (event) => console.log('Clicked!');
element.addEventListener('click', handler);
element.removeEventListener('click', handler);
// 3. Event Object Properties
element.addEventListener('click', (event) => {
console.log(event.target); // Element that triggered the event
console.log(event.currentTarget); // Element that listener is attached to
console.log(event.type); // Type of event (e.g., 'click')
console.log(event.timeStamp); // Time when event occurred
event.preventDefault(); // Prevent default behavior
event.stopPropagation(); // Stop event bubbling
});

Common Events

// Mouse Events
element.addEventListener('click', () => {}); // Mouse click
element.addEventListener('dblclick', () => {}); // Double click
element.addEventListener('mouseenter', () => {}); // Mouse enters element
element.addEventListener('mouseleave', () => {}); // Mouse leaves element
element.addEventListener('mousemove', () => {}); // Mouse moves
// Keyboard Events
document.addEventListener('keydown', (e) => { // Key pressed
console.log(e.key); // Key value
console.log(e.code); // Physical key
});
document.addEventListener('keyup', () => {}); // Key released
// Form Events
form.addEventListener('submit', (e) => { // Form submission
e.preventDefault(); // Prevent form submission
});
input.addEventListener('change', () => {}); // Input value changes
input.addEventListener('input', () => {}); // Real-time input changes
input.addEventListener('focus', () => {}); // Input receives focus
input.addEventListener('blur', () => {}); // Input loses focus
// Document/Window Events
window.addEventListener('load', () => {}); // Page fully loaded
document.addEventListener('DOMContentLoaded', () => {}); // DOM ready
window.addEventListener('resize', () => {}); // Window resized
window.addEventListener('scroll', () => {}); // Page scrolled

Event Propagation

Events in the DOM bubble up through the ancestor elements (event bubbling) and can also capture down (event capturing).

// Event Bubbling (default)
parent.addEventListener('click', () => {
console.log('Parent clicked');
});
child.addEventListener('click', () => {
console.log('Child clicked');
// Outputs: Child clicked, then Parent clicked
});
// Event Capturing (third parameter true)
parent.addEventListener('click', () => {
console.log('Parent clicked');
}, true);
child.addEventListener('click', () => {
console.log('Child clicked');
// Outputs: Parent clicked, then Child clicked
}, true);
// Stop Propagation
child.addEventListener('click', (e) => {
e.stopPropagation(); // Prevents event from bubbling up
console.log('Child only');
});

Event Delegation

Event delegation is a technique of handling events on multiple elements using a single event listener on their parent.

// Instead of adding listeners to each button
document.getElementById('button-container').addEventListener('click', (e) => {
if (e.target.matches('button')) {
console.log('Button clicked:', e.target.textContent);
}
});
// Example HTML:
// <div id="button-container">
// <button>Button 1</button>
// <button>Button 2</button>
// <button>Button 3</button>
// </div>

Async

  • JavaScript is synchronous and single-threaded, meaning it can only execute one task at a time.
  • Execution of code is blocked until the current task is completed.
  • example:
    • synchronous code is useful when registering a user in a database and displaying a success message only after the database operation is complete.
  • Asynchronous programming is a technique that allows multiple tasks to be performed simultaneously without blocking the main thread.
  • Execution of code is not blocked, allowing other tasks to run in the background.
  • It’s essential for handling time-consuming tasks like network requests, file operations, and animations without freezing the page.
  • example:
    • Asynchronous code is useful when fetching data from a server and updating the UI without blocking the main thread.
┌────────── JavaScript Engine ─────────┐ ┌────── Web APIs ─────┐
│ │ │ │
│ ┌─────────┐ ┌──────────┐ │ │ • DOM │
│ │ │ │ │ │ │ • setTimeout │
│ │ Heap │ │ Call │ │ │ • fetch │
│ │ │ │ Stack │ │ │ • addEventListener │
│ │ │ │ │ │ │ • XMLHttpRequest │
│ │ │ │ fn2() │ │ │ • geolocation │
│ │ │ │ fn1() │ │ │ • localStorage │
│ │ │ │ main() │ │ │ │
│ │ │ │ global │ │ │ │
│ └─────────┘ └──────────┘ │ │ │
└──────────────────────────────────────┘ └─────────────────────┘
┌──────────────────┐ ┌─────────────────────────┐ ┌──────────────────────┐
│ │ │ │ │ Microtask Queue │
│ Event Loop │ │ Callback Queue │ │ [Promise] │
│ ↺ │ │ [fn3()][fn4()][fn5()] │ │ [queueMicrotask] │
│ │ │ │ │ │
└──────────────────┘ └─────────────────────────┘ └──────────────────────┘
  • Event Loop controls the execution of tasks based on the priority of the tasks.
  • Microtask Queue has higher priority than Callback Queue.
  • Microtask Queue includes Promises and queueMicrotask,fetch.
  • Callback Queue includes all the callbacks from the Web APIs.

Example

setTimeout(() => {
console.log('callback');
}, 0);
Promise.resolve().then(() => console.log('promise'));
console.log('synchronous');
// Output: synchronous promise callback
// Explanation:
// 1. 'synchronous' logs first (direct execution)
// 2. 'promise' logs second (microtask queue priority)
// 3. 'callback' logs last (callback queue)
Promise.resolve().then(() => console.log(1));
setTimeout(() => console.log(2), 0);
queueMicrotask(() =>{
console.log(3)
queueMicrotask(() => console.log(4))
})
console.log(5);
// Output: 5 1 3 4 2

Promise

  • A Promise is an object that represents a value that may not be available yet but will be at some point.
  • It can be in one of three states: pending, fulfilled, or rejected.
  • A Promise can be created using the Promise constructor.
  • A Promise can be resolved or rejected using the resolve and reject methods.
  • A Promise can be chained using the then method.
  • A Promise can be handled using the catch method.
const promise = new Promise((resolve, reject) => {
// ...
});

Creation

  • A Promise is created using the Promise constructor.
  • The constructor takes a function as an argument, which takes two parameters: resolve and reject.
  • resolve is a function that is called when the Promise is fulfilled.
  • reject is a function that is called when the Promise is rejected.

usecase:

  • Simulating network delay
  • Fetching data from a server
  • Reading from a file
// Basic Promise Creation
const promise = new Promise((resolve, reject) => {
// Async operation
const success = true;
if (success) {
resolve('Operation successful!');
} else {
reject(new Error('Operation failed!'));
}
});
// Promise that fetches data
function fetchUserData(userId) {
return new Promise((resolve, reject) => {
fetch(`https://api.example.com/users/${userId}`)
.then(response => response.json())
.then(data => resolve(data))
.catch(error => reject(error));
});
}

Consuming Promises

  • Promises can be consumed using the then method.
  • The then method takes two arguments: a function that is called when the Promise is fulfilled and a function that is called when the Promise is rejected.
  • The catch method can be used to handle errors.
  • The finally method can be used to perform cleanup after the Promise is resolved or rejected.

usecase:

  • Handling asynchronous operations
  • Chaining operations
  • Error handling
// Using .then() and .catch()
promise
.then(result => {
console.log('Success:', result);
})
.catch(error => {
console.error('Error:', error);
})
.finally(() => {
console.log('Promise completed');
});
// Async/await syntax (modern approach)
async function handlePromise() {
try {
const result = await promise;
console.log('Success:', result);
} catch (error) {
console.error('Error:', error);
}
}
// Real-world example
async function loadUserData() {
try {
const userData = await fetchUserData(1);
const data = await userData.json(); // Convert response to JSON takes time
console.log('User data:', data);
} catch (error) {
console.error('Failed to load user:', error);
}
}

Promisification

  • Promisification is the process of converting a function that takes a callback into a function that returns a promise.
  • It’s useful for handling asynchronous operations in a more readable and manageable way.
function getCurrentPosition() {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
}
async function getLocation() {
try {
const position = await getCurrentPosition();
console.log(position);
} catch (error) {
console.error(error);
}
}
// Usage
getLocation(); // Output: position object or error
// Do not forget to handle errors!

Fetch

The Fetch API provides a modern interface for making HTTP requests. It returns Promises, making it easier to handle asynchronous operations.

Basic Usage

// Basic GET request
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// Using async/await
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
}

Request Options

// POST request with JSON data
fetch('https://api.example.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token-here'
},
body: JSON.stringify({
title: 'New Post',
content: 'Post content'
})
});
// Form data
const formData = new FormData();
formData.append('file', fileInput.files[0]);
fetch('https://api.example.com/upload', {
method: 'POST',
body: formData
});

Response Handling

fetch('https://api.example.com/data')
.then(response => {
// Check if response is ok (status 200-299)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Check response type and handle accordingly
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return response.json();
} else if (contentType && contentType.includes('text/plain')) {
return response.text();
} else if (contentType && contentType.includes('application/pdf')) {
return response.blob();
}
throw new TypeError("Unsupported data format");
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));