Master the essential building blocks of JavaScript programming from variables to DOM manipulation
What You'll Learn
Understand JavaScript syntax, variables, and data types
Master control flow structures, functions, and scope
Work with objects, arrays, and modern ES6+ features
Manipulate the DOM and handle browser events
Write clean, maintainable JavaScript code following best practices
Build interactive web applications with JavaScript
1. Introduction to JavaScript
What is JavaScript?
JavaScript is a versatile, high-level programming language that brings interactivity to websites. Originally designed to run in web browsers, JavaScript has evolved into a full-featured language used for:
Front-End Development
Create interactive user interfaces, validate forms, handle events, and build single-page applications (SPAs) with frameworks like React, Vue, and Angular.
Back-End Development
Build server-side applications with Node.js, create REST APIs, manage databases, and handle server logic with the same language you use on the front-end.
Mobile Development
Develop cross-platform mobile apps using React Native, Ionic, or NativeScript, sharing code between iOS and Android platforms.
Desktop Applications
Create desktop apps with Electron (used by VS Code, Slack, Discord) that run on Windows, macOS, and Linux.
Key Characteristics
Interpreted Language
JavaScript code is executed line-by-line by the JavaScript engine (like V8 in Chrome) without prior compilation, making development fast and flexible.
Dynamically Typed
Variables don't require explicit type declarations and can hold any type of value. The type is determined at runtime.
Event-Driven
JavaScript responds to user actions (clicks, keyboard input, mouse movements) through event handlers, making web pages interactive.
Asynchronous
JavaScript can handle multiple operations simultaneously using callbacks, promises, and async/await, enabling smooth user experiences.
Brief History
1995: Created by Brendan Eich at Netscape in just 10 days
1997: Standardized as ECMAScript (ES1)
2009: Node.js released, bringing JavaScript to the server
2015: ES6/ES2015 introduced major improvements (arrow functions, classes, modules)
Present: Annual ECMAScript updates with new features
2. Setting Up Your Environment
Running JavaScript
JavaScript can run in multiple environments. Here are the most common ways to get started:
1. Browser Console (Immediate Start)
Quick Start: Open any modern browser (Chrome, Firefox, Edge), press F12, and navigate to the Console tab. You can type JavaScript directly and see results instantly.
// Try this in your browser console
console.log("Hello, JavaScript!");
alert("This is an alert box");
console.table([1, 2, 3, 4, 5]);
2. HTML File with Inline JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My First JavaScript</title>
</head>
<body>
<h1>JavaScript Demo</h1>
<script>
// JavaScript code goes here
console.log("This runs when the page loads");
document.write("<p>Generated by JavaScript</p>");
</script>
</body>
</html>
Best Practice: Place <script> tags at the end of the <body> tag (or use defer/async attributes) to ensure the HTML loads before JavaScript executes. This improves page load performance and prevents errors from trying to access elements that haven't loaded yet.
4. Node.js (Server-Side JavaScript)
// Install Node.js from nodejs.org
// Create a file named app.js with:
console.log("Hello from Node.js!");
// Run it in terminal:
// node app.js
JavaScript provides three ways to declare variables: var, let, and const.
let - Block-Scoped Variable
let age = 25;
let name = "Alice";
if (true) {
let age = 30; // Different variable
console.log(age); // 30
}
console.log(age); // 25 - original unchanged
Use let for variables that will change. It's block-scoped (only exists within { } brackets).
const - Constant Reference
const PI = 3.14159;
const MAX_USERS = 100;
// This will cause an error:
// PI = 3.14; // Error!
// But objects can be modified:
const person = { name: "Bob" };
person.name = "Alice"; // This is OK!
person.age = 30; // This is OK!
Use const for values that won't be reassigned. The reference is constant, but objects can still be modified.
Avoid var: The old var keyword has confusing scoping rules (function-scoped instead of block-scoped) and can lead to bugs. Use let and const instead in modern JavaScript.
Data Types
JavaScript has 8 fundamental data types:
1. Number
// Integers and floating-point numbers
let integer = 42;
let float = 3.14159;
let negative = -10;
let exponential = 2.5e6; // 2,500,000
// Special numeric values
let infinity = Infinity;
let negInfinity = -Infinity;
let notANumber = NaN; // "Not a Number"
// Number operations
console.log(10 / 3); // 3.3333333333333335
console.log(Math.PI); // 3.141592653589793
console.log(Math.max(5, 10, 15)); // 15
2. String
// Three ways to create strings
let single = 'Single quotes';
let double = "Double quotes";
let template = `Template literal`;
// Template literals (ES6+) - powerful!
let name = "Alice";
let age = 25;
let greeting = `Hello, ${name}! You are ${age} years old.`;
console.log(greeting); // "Hello, Alice! You are 25 years old."
// Multi-line strings
let multiline = `
This is a
multi-line
string
`;
// String methods
let text = "JavaScript";
console.log(text.length); // 10
console.log(text.toUpperCase()); // "JAVASCRIPT"
console.log(text.slice(0, 4)); // "Java"
3. Boolean
let isActive = true;
let isLoggedIn = false;
// Boolean from comparisons
let isAdult = age >= 18; // true or false
let hasAccess = isLoggedIn && isActive;
// Truthy and Falsy values
// Falsy: false, 0, "", null, undefined, NaN
// Everything else is truthy
if ("Hello") { // truthy
console.log("This runs!");
}
if (0) { // falsy
console.log("This doesn't run");
}
4. Undefined
let uninitializedVar;
console.log(uninitializedVar); // undefined
function noReturn() {
// no return statement
}
console.log(noReturn()); // undefined
5. Null
let emptyValue = null; // Intentionally empty
let user = null; // No user currently logged in
// null vs undefined
console.log(null == undefined); // true (loose equality)
console.log(null === undefined); // false (strict equality)
6. BigInt (Large Integers)
// For numbers larger than Number.MAX_SAFE_INTEGER
let bigNumber = 9007199254740991n;
let anotherBig = BigInt("9007199254740992");
console.log(bigNumber + 1n); // 9007199254740992n
7. Symbol (Unique Identifiers)
let sym1 = Symbol("description");
let sym2 = Symbol("description");
console.log(sym1 === sym2); // false - always unique
8. Object (Complex Data)
// Object literal
let person = {
name: "Alice",
age: 30,
city: "New York"
};
// Array (special type of object)
let colors = ["red", "green", "blue"];
// Function (also an object)
function greet(name) {
return `Hello, ${name}!`;
}
// String to Number
let strNum = "42";
let num1 = Number(strNum); // 42
let num2 = parseInt(strNum); // 42
let num3 = parseFloat("3.14"); // 3.14
let num4 = +"42"; // 42 (unary plus)
// Number to String
let num = 42;
let str1 = String(num); // "42"
let str2 = num.toString(); // "42"
let str3 = "" + num; // "42"
// To Boolean
let bool1 = Boolean(1); // true
let bool2 = Boolean(0); // false
let bool3 = !!"text"; // true (double negation)
4. Operators and Expressions
Arithmetic Operators
// Basic arithmetic
let sum = 10 + 5; // 15 - Addition
let difference = 10 - 5; // 5 - Subtraction
let product = 10 * 5; // 50 - Multiplication
let quotient = 10 / 5; // 2 - Division
let remainder = 10 % 3; // 1 - Modulus (remainder)
let power = 2 ** 3; // 8 - Exponentiation
// Increment and Decrement
let counter = 0;
counter++; // 1 - Post-increment
++counter; // 2 - Pre-increment
counter--; // 1 - Post-decrement
--counter; // 0 - Pre-decrement
// Compound assignment
let x = 10;
x += 5; // x = x + 5 → 15
x -= 3; // x = x - 3 → 12
x *= 2; // x = x * 2 → 24
x /= 4; // x = x / 4 → 6
Comparison Operators
// Equality comparisons
console.log(5 == "5"); // true - loose equality (type coercion)
console.log(5 === "5"); // false - strict equality (no coercion)
console.log(5 != "5"); // false - loose inequality
console.log(5 !== "5"); // true - strict inequality
// Relational comparisons
console.log(10 > 5); // true - greater than
console.log(10 < 5); // false - less than
console.log(10 >= 10); // true - greater than or equal
console.log(10 <= 5); // false - less than or equal
Best Practice: Always use strict equality (===) and strict inequality (!==) to avoid unexpected type coercion bugs.
Logical Operators
// AND (&&) - Both must be true
let isAdult = age >= 18;
let hasLicense = true;
let canDrive = isAdult && hasLicense;
// OR (||) - At least one must be true
let isWeekend = day === "Saturday" || day === "Sunday";
// NOT (!) - Inverts boolean
let isNotLoggedIn = !isLoggedIn;
// Short-circuit evaluation
let userName = userInput || "Guest"; // Default value
let data = apiResponse && apiResponse.data; // Safe access
Ternary Operator
// Shorthand for if-else
let age = 20;
let status = age >= 18 ? "Adult" : "Minor";
// Can be nested (but don't overdo it!)
let price = isMember
? (isPremium ? 50 : 75)
: 100;
String Operators
// Concatenation with +
let greeting = "Hello" + " " + "World"; // "Hello World"
let message = "Count: " + 42; // "Count: 42"
// Template literals (preferred)
let name = "Alice";
let welcome = `Welcome, ${name}!`; // "Welcome, Alice!"
Nullish Coalescing (??) - ES2020
// Returns right side if left is null or undefined
let userInput = null;
let value = userInput ?? "Default"; // "Default"
// Different from || which checks for falsy
let count = 0;
let result1 = count || 10; // 10 (0 is falsy)
let result2 = count ?? 10; // 0 (0 is not null/undefined)
Optional Chaining (?.) - ES2020
// Safe property access
let user = {
name: "Alice",
address: {
city: "New York"
}
};
// Without optional chaining (can throw error)
// let zip = user.address.zipCode.value; // Error!
// With optional chaining (returns undefined)
let city = user?.address?.city; // "New York"
let zip = user?.address?.zipCode; // undefined (no error!)
5. Control Flow Structures
If-Else Statements
let age = 20;
// Simple if
if (age >= 18) {
console.log("You are an adult");
}
// If-else
if (age >= 18) {
console.log("You are an adult");
} else {
console.log("You are a minor");
}
// If-else-if chain
if (age >= 65) {
console.log("Senior citizen");
} else if (age >= 18) {
console.log("Adult");
} else if (age >= 13) {
console.log("Teenager");
} else {
console.log("Child");
}
Switch Statements
let day = "Monday";
switch (day) {
case "Monday":
console.log("Start of work week");
break;
case "Tuesday":
case "Wednesday":
case "Thursday":
console.log("Midweek");
break;
case "Friday":
console.log("TGIF!");
break;
case "Saturday":
case "Sunday":
console.log("Weekend!");
break;
default:
console.log("Invalid day");
}
Don't Forget break: Without break, execution "falls through" to the next case. This is usually a bug, though it can be used intentionally.
Loops
For Loop
// Classic for loop
for (let i = 0; i < 5; i++) {
console.log(`Iteration ${i}`);
}
// Loop through array
let colors = ["red", "green", "blue"];
for (let i = 0; i < colors.length; i++) {
console.log(colors[i]);
}
While Loop
// While loop
let count = 0;
while (count < 5) {
console.log(`Count: ${count}`);
count++;
}
// Do-while loop (always runs at least once)
let password;
do {
password = prompt("Enter password:");
} while (password !== "secret");
For...of Loop (ES6)
// Iterate over array values
let fruits = ["apple", "banana", "orange"];
for (let fruit of fruits) {
console.log(fruit); // "apple", "banana", "orange"
}
// Works with strings too
for (let char of "Hello") {
console.log(char); // "H", "e", "l", "l", "o"
}
For...in Loop
// Iterate over object keys
let person = {
name: "Alice",
age: 30,
city: "New York"
};
for (let key in person) {
console.log(`${key}: ${person[key]}`);
}
// Output:
// name: Alice
// age: 30
// city: New York
Modern Approach: Use for...of for arrays and Object.entries() for objects in modern code.
Loop Control
// break - Exit loop entirely
for (let i = 0; i < 10; i++) {
if (i === 5) break;
console.log(i); // 0, 1, 2, 3, 4
}
// continue - Skip to next iteration
for (let i = 0; i < 5; i++) {
if (i === 2) continue;
console.log(i); // 0, 1, 3, 4 (skips 2)
}
6. Functions and Scope
Function Declarations
// Traditional function declaration
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Alice")); // "Hello, Alice!"
// Function with multiple parameters
function add(a, b) {
return a + b;
}
// Default parameters (ES6)
function welcome(name = "Guest", time = "morning") {
return `Good ${time}, ${name}!`;
}
console.log(welcome()); // "Good morning, Guest!"
console.log(welcome("Alice", "evening")); // "Good evening, Alice!"
Function Expressions
// Function expression (stored in variable)
const multiply = function(a, b) {
return a * b;
};
console.log(multiply(5, 3)); // 15
// Can be anonymous or named
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1); // Can call itself by name
};
Arrow Functions (ES6)
// Arrow function syntax
const square = (x) => x * x;
// Multiple parameters
const add = (a, b) => a + b;
// No parameters
const greet = () => "Hello!";
// Single parameter (can omit parentheses)
const double = x => x * 2;
// Multiple statements (need curly braces and return)
const calculate = (a, b) => {
const sum = a + b;
const average = sum / 2;
return average;
};
// Object literal (wrap in parentheses)
const makePerson = (name, age) => ({
name: name,
age: age
});
Arrow Functions vs Regular Functions: Arrow functions don't have their own this binding, making them perfect for callbacks and array methods. Use regular functions for methods that need this.
Rest Parameters
// Rest parameter (...) collects remaining arguments
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
// Mix with regular parameters
function introduce(greeting, ...names) {
return `${greeting}, ${names.join(" and ")}!`;
}
console.log(introduce("Hello", "Alice", "Bob")); // "Hello, Alice and Bob!"
Spread Operator
// Spread operator expands array
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// Works with objects too
let person = { name: "Alice", age: 30 };
let employee = { ...person, job: "Developer" };
// { name: "Alice", age: 30, job: "Developer" }
// Pass array elements as arguments
let numbers = [5, 10, 15];
console.log(Math.max(...numbers)); // 15
Scope and Closures
// Global scope
let globalVar = "I'm global";
function outer() {
// Function scope
let outerVar = "I'm in outer";
function inner() {
// Nested scope - has access to outer variables
let innerVar = "I'm in inner";
console.log(globalVar); // Accessible
console.log(outerVar); // Accessible
console.log(innerVar); // Accessible
}
inner();
// console.log(innerVar); // Error! Not accessible
}
outer();
// Closure example
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
let counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
What is a Closure?
A closure is a function that has access to variables in its outer scope, even after the outer function has returned. This allows for data privacy and factory patterns.
Higher-Order Functions
// Function that accepts another function
function repeat(n, action) {
for (let i = 0; i < n; i++) {
action(i);
}
}
repeat(3, console.log); // Logs: 0, 1, 2
// Function that returns a function
function multiplyBy(factor) {
return function(number) {
return number * factor;
};
}
let double = multiplyBy(2);
let triple = multiplyBy(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
let person = { name: "Alice", age: 30 };
// Add property
person.email = "alice@example.com";
person["phone"] = "555-1234";
// Modify property
person.age = 31;
// Delete property
delete person.phone;
// Check if property exists
console.log("name" in person); // true
console.log("phone" in person); // false
console.log(person.hasOwnProperty("age")); // true
// Get all keys
console.log(Object.keys(person)); // ["name", "age", "email"]
// Get all values
console.log(Object.values(person)); // ["Alice", 31, "alice@example.com"]
// Get key-value pairs
console.log(Object.entries(person));
// [["name", "Alice"], ["age", 31], ["email", "alice@example.com"]]
Object Destructuring (ES6)
let person = {
name: "Alice",
age: 30,
city: "New York"
};
// Extract properties into variables
let { name, age } = person;
console.log(name); // "Alice"
console.log(age); // 30
// Rename variables
let { name: fullName, age: years } = person;
console.log(fullName); // "Alice"
// Default values
let { country = "USA" } = person;
console.log(country); // "USA"
// Nested destructuring
let user = {
id: 1,
profile: {
username: "alice123",
bio: "Developer"
}
};
let { profile: { username } } = user;
console.log(username); // "alice123"
Arrays
Creating and Accessing Arrays
// Array literal
let fruits = ["apple", "banana", "orange"];
// Access by index (0-based)
console.log(fruits[0]); // "apple"
console.log(fruits[1]); // "banana"
// Get array length
console.log(fruits.length); // 3
// Last element
console.log(fruits[fruits.length - 1]); // "orange"
// Mixed types (not recommended, but possible)
let mixed = [1, "text", true, { key: "value" }];
Array Methods - Modifying
let fruits = ["apple", "banana"];
// Add to end
fruits.push("orange"); // ["apple", "banana", "orange"]
// Remove from end
let last = fruits.pop(); // "orange"
// Add to beginning
fruits.unshift("mango"); // ["mango", "apple", "banana"]
// Remove from beginning
let first = fruits.shift(); // "mango"
// Add/remove at specific position
// splice(startIndex, deleteCount, ...itemsToAdd)
fruits.splice(1, 0, "grape"); // ["apple", "grape", "banana"]
fruits.splice(1, 1); // ["apple", "banana"] - removes "grape"
// Copy array (doesn't modify original)
let copy = fruits.slice(); // ["apple", "banana"]
let partial = fruits.slice(0, 1); // ["apple"]
Array Methods - Iterating
let numbers = [1, 2, 3, 4, 5];
// forEach - Execute function for each element
numbers.forEach((num, index) => {
console.log(`Index ${index}: ${num}`);
});
// map - Transform each element, return new array
let doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter - Keep elements that pass test
let evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]
// reduce - Reduce array to single value
let sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum); // 15
// find - Get first element that matches
let found = numbers.find(num => num > 3);
console.log(found); // 4
// findIndex - Get index of first match
let index = numbers.findIndex(num => num > 3);
console.log(index); // 3
// some - Check if at least one passes test
let hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
// every - Check if all pass test
let allPositive = numbers.every(num => num > 0);
console.log(allPositive); // true
Array Methods - Searching and Sorting
let fruits = ["apple", "banana", "orange", "apple"];
// indexOf - Find first occurrence
console.log(fruits.indexOf("apple")); // 0
console.log(fruits.indexOf("grape")); // -1 (not found)
// lastIndexOf - Find last occurrence
console.log(fruits.lastIndexOf("apple")); // 3
// includes - Check if array contains value
console.log(fruits.includes("banana")); // true
// sort - Sort in place (modifies original!)
let numbers = [3, 1, 4, 1, 5, 9];
numbers.sort(); // [1, 1, 3, 4, 5, 9]
// Custom sort
let people = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
{ name: "Charlie", age: 35 }
];
people.sort((a, b) => a.age - b.age); // Sort by age
// reverse - Reverse array order
numbers.reverse(); // [9, 5, 4, 3, 1, 1]
Array Destructuring (ES6)
let colors = ["red", "green", "blue"];
// Extract into variables
let [first, second, third] = colors;
console.log(first); // "red"
// Skip elements
let [primary, , tertiary] = colors;
console.log(primary); // "red"
console.log(tertiary); // "blue"
// Rest pattern
let [head, ...rest] = colors;
console.log(head); // "red"
console.log(rest); // ["green", "blue"]
// Default values
let [a, b, c, d = "yellow"] = colors;
console.log(d); // "yellow"
Spread Operator with Arrays
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
// Combine arrays
let combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// Copy array
let copy = [...arr1]; // [1, 2, 3]
// Add elements
let extended = [0, ...arr1, 4]; // [0, 1, 2, 3, 4]
// Convert string to array
let chars = [..."Hello"]; // ["H", "e", "l", "l", "o"]
8. DOM Manipulation
What is the DOM?
Document Object Model
The DOM is a programming interface that represents HTML/XML documents as a tree structure. JavaScript can access and manipulate this tree to change the content, structure, and styling of web pages dynamically.
Selecting Elements
// By ID (returns single element or null)
let header = document.getElementById("main-header");
// By class name (returns HTMLCollection)
let buttons = document.getElementsByClassName("btn");
// By tag name (returns HTMLCollection)
let paragraphs = document.getElementsByTagName("p");
// Query selector - CSS selector (returns first match)
let firstButton = document.querySelector(".btn");
let nav = document.querySelector("#navigation");
// Query selector all - CSS selector (returns NodeList)
let allButtons = document.querySelectorAll(".btn");
let links = document.querySelectorAll("a[href^='http']");
Modern Best Practice: Use querySelector() and querySelectorAll() for most DOM selection tasks. They're more flexible and work like CSS selectors.
Modifying Content
let element = document.querySelector("#content");
// Change text content (safe, no HTML)
element.textContent = "New text content";
// Change HTML content (can include HTML tags)
element.innerHTML = "<strong>Bold text</strong>";
// Get/set input values
let input = document.querySelector("#username");
console.log(input.value); // Get value
input.value = "New value"; // Set value
Security Warning: Be careful with innerHTML when using user input - it can lead to XSS attacks. Use textContent for plain text.
Modifying Attributes
let img = document.querySelector("img");
// Get attribute
let src = img.getAttribute("src");
// Set attribute
img.setAttribute("src", "new-image.jpg");
img.setAttribute("alt", "Description");
// Remove attribute
img.removeAttribute("title");
// Check if has attribute
if (img.hasAttribute("alt")) {
console.log("Has alt text");
}
// Direct property access (common attributes)
img.src = "image.jpg";
img.alt = "My image";
img.className = "large-img";
img.id = "hero-image";
Modifying Styles
let box = document.querySelector(".box");
// Inline styles (camelCase for properties)
box.style.color = "red";
box.style.backgroundColor = "blue";
box.style.fontSize = "20px";
box.style.display = "none"; // Hide element
// Get computed styles (including CSS)
let styles = window.getComputedStyle(box);
console.log(styles.color); // Current color
Working with Classes
let element = document.querySelector(".item");
// Add class
element.classList.add("active");
element.classList.add("highlight", "visible"); // Multiple
// Remove class
element.classList.remove("hidden");
// Toggle class (add if absent, remove if present)
element.classList.toggle("expanded");
// Check if has class
if (element.classList.contains("active")) {
console.log("Element is active");
}
// Replace class
element.classList.replace("old-class", "new-class");
Creating and Removing Elements
// Create new element
let newDiv = document.createElement("div");
newDiv.textContent = "I'm a new div!";
newDiv.classList.add("box");
// Append to parent (end)
let container = document.querySelector("#container");
container.appendChild(newDiv);
// Insert before (beginning)
container.insertBefore(newDiv, container.firstChild);
// Modern methods
container.append(newDiv); // Append (can add multiple)
container.prepend(newDiv); // Prepend (to start)
container.before(newDiv); // Before container
container.after(newDiv); // After container
// Remove element
newDiv.remove(); // Remove itself
// Or from parent
container.removeChild(newDiv);
// Replace element
let oldElement = document.querySelector(".old");
let newElement = document.createElement("div");
oldElement.replaceWith(newElement);
Traversing the DOM
let element = document.querySelector(".item");
// Parent
let parent = element.parentElement;
let parentNode = element.parentNode;
// Children
let children = element.children; // HTMLCollection
let firstChild = element.firstElementChild;
let lastChild = element.lastElementChild;
// Siblings
let nextSib = element.nextElementSibling;
let prevSib = element.previousElementSibling;
// Closest ancestor matching selector
let section = element.closest("section");
// All matching descendants
let links = element.querySelectorAll("a");
9. Events and Event Handling
What are Events?
Events are actions or occurrences that happen in the browser that JavaScript can detect and respond to. They enable interactivity in web applications.
Adding Event Listeners
// Get element
let button = document.querySelector("#myButton");
// Add event listener (modern approach)
button.addEventListener("click", function(event) {
console.log("Button clicked!");
console.log(event); // Event object with details
});
// Arrow function version
button.addEventListener("click", (event) => {
console.log("Clicked!");
});
// Named function (can be removed later)
function handleClick(event) {
console.log("Clicked!");
}
button.addEventListener("click", handleClick);
// Remove event listener
button.removeEventListener("click", handleClick);
Avoid: Don't use inline event handlers like onclick="doSomething()" in HTML. Use addEventListener() instead for separation of concerns and flexibility.
Common Events
Mouse Events
click - Element clicked
dblclick - Double-clicked
mousedown - Mouse button pressed
mouseup - Mouse button released
mousemove - Mouse moved
mouseenter - Mouse enters element
mouseleave - Mouse leaves element
mouseover - Mouse over element (bubbles)
contextmenu - Right-click
Keyboard Events
keydown - Key pressed down
keyup - Key released
keypress - Key pressed (deprecated)
Form Events
submit - Form submitted
input - Input value changed
change - Input changed and blurred
focus - Element focused
blur - Element unfocused
Document Events
DOMContentLoaded - DOM loaded
load - Page fully loaded
resize - Window resized
scroll - Page scrolled
beforeunload - Before leaving page
Event Object
button.addEventListener("click", function(event) {
// Event type
console.log(event.type); // "click"
// Target element (where event happened)
console.log(event.target);
// Current target (element with listener)
console.log(event.currentTarget);
// Mouse position
console.log(event.clientX, event.clientY);
// Prevent default behavior
event.preventDefault();
// Stop event from bubbling
event.stopPropagation();
});
// Keyboard event
document.addEventListener("keydown", (event) => {
console.log(event.key); // Key name: "a", "Enter", "ArrowUp"
console.log(event.code); // Physical key: "KeyA", "Enter", "ArrowUp"
console.log(event.ctrlKey); // true if Ctrl pressed
console.log(event.shiftKey); // true if Shift pressed
console.log(event.altKey); // true if Alt pressed
});
Practical Examples
Example 1: Click Counter
let count = 0;
let button = document.querySelector("#counterBtn");
let display = document.querySelector("#count");
button.addEventListener("click", () => {
count++;
display.textContent = count;
});
Example 2: Form Validation
let form = document.querySelector("#myForm");
form.addEventListener("submit", (event) => {
event.preventDefault(); // Stop form submission
let email = document.querySelector("#email").value;
let password = document.querySelector("#password").value;
// Validate
if (!email.includes("@")) {
alert("Invalid email");
return;
}
if (password.length < 8) {
alert("Password must be at least 8 characters");
return;
}
// If valid, submit
console.log("Form is valid!", { email, password });
// form.submit(); // Actually submit
});
Example 3: Keyboard Navigation
document.addEventListener("keydown", (event) => {
let box = document.querySelector("#movableBox");
let step = 10; // pixels to move
switch(event.key) {
case "ArrowUp":
event.preventDefault();
box.style.top = (box.offsetTop - step) + "px";
break;
case "ArrowDown":
event.preventDefault();
box.style.top = (box.offsetTop + step) + "px";
break;
case "ArrowLeft":
box.style.left = (box.offsetLeft - step) + "px";
break;
case "ArrowRight":
box.style.left = (box.offsetLeft + step) + "px";
break;
}
});
Example 4: Live Search
let searchInput = document.querySelector("#search");
let items = document.querySelectorAll(".item");
searchInput.addEventListener("input", (event) => {
let query = event.target.value.toLowerCase();
items.forEach(item => {
let text = item.textContent.toLowerCase();
if (text.includes(query)) {
item.style.display = "block"; // Show
} else {
item.style.display = "none"; // Hide
}
});
});
Event Delegation
// Instead of adding listeners to many elements:
// let buttons = document.querySelectorAll(".btn");
// buttons.forEach(btn => btn.addEventListener("click", handleClick));
// Add ONE listener to parent (more efficient)
let container = document.querySelector("#buttonContainer");
container.addEventListener("click", (event) => {
// Check if clicked element is a button
if (event.target.classList.contains("btn")) {
console.log("Button clicked:", event.target.textContent);
}
});
// Works for dynamically added buttons too!
Event Delegation: Use event delegation when you have many similar elements or when elements are added dynamically. It's more efficient and handles new elements automatically.
10. Modern JavaScript (ES6+)
Template Literals
let name = "Alice";
let age = 30;
// Old way (concatenation)
let message1 = "Hello, " + name + "! You are " + age + " years old.";
// ES6 way (template literal)
let message2 = `Hello, ${name}! You are ${age} years old.`;
// Multi-line strings
let html = `
<div class="card">
<h2>${name}</h2>
<p>Age: ${age}</p>
</div>
`;
// Expression evaluation
let price = 19.99;
let quantity = 3;
console.log(`Total: $${(price * quantity).toFixed(2)}`); // "Total: $59.97"
Destructuring Assignment
// Array destructuring
let [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [3, 4, 5]
// Object destructuring
let person = { name: "Alice", age: 30, city: "NYC" };
let { name, age } = person;
// Function parameters
function greet({ name, age }) {
return `Hello ${name}, you are ${age} years old`;
}
greet(person); // "Hello Alice, you are 30 years old"
Default Parameters
// Old way
function greet(name) {
name = name || "Guest";
return `Hello, ${name}!`;
}
// ES6 way
function greet(name = "Guest") {
return `Hello, ${name}!`;
}
// Complex defaults
function createUser(name, role = "user", active = true) {
return { name, role, active };
}
Object Property Shorthand
let name = "Alice";
let age = 30;
// Old way
let person1 = {
name: name,
age: age
};
// ES6 shorthand (when key name matches variable)
let person2 = { name, age };
// Method shorthand
let person3 = {
name,
age,
// Old way
greet: function() {
return `Hello, ${this.name}`;
},
// ES6 way
sayBye() {
return `Goodbye, ${this.name}`;
}
};
Classes (ES6)
// Class declaration
class Person {
// Constructor
constructor(name, age) {
this.name = name;
this.age = age;
}
// Method
greet() {
return `Hello, I'm ${this.name}`;
}
// Getter
get info() {
return `${this.name}, ${this.age}`;
}
// Setter
set birthYear(year) {
this.age = new Date().getFullYear() - year;
}
// Static method
static create(name, age) {
return new Person(name, age);
}
}
// Create instance
let alice = new Person("Alice", 30);
console.log(alice.greet()); // "Hello, I'm Alice"
// Inheritance
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // Call parent constructor
this.grade = grade;
}
// Override method
greet() {
return `${super.greet()}, I'm a student`;
}
}
Modules (Import/Export)
// math.js - Export
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export const PI = 3.14159;
// Default export
export default function multiply(a, b) {
return a * b;
}
// app.js - Import
import multiply from './math.js'; // Default import
import { add, subtract, PI } from './math.js'; // Named imports
import * as Math from './math.js'; // Import all
console.log(add(5, 3)); // 8
console.log(Math.PI); // 3.14159
Promises and Async/Await
// Promise
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let data = { name: "Alice", age: 30 };
resolve(data); // Success
// reject(new Error("Failed")); // Error
}, 1000);
});
}
// Using Promise
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
// Async/await (cleaner syntax)
async function getData() {
try {
let data = await fetchData();
console.log(data);
} catch (error) {
console.error(error);
}
}
// Multiple async operations
async function fetchMultiple() {
try {
// Sequential (one after another)
let user = await fetchUser();
let posts = await fetchPosts(user.id);
// Parallel (at the same time)
let [user2, posts2] = await Promise.all([
fetchUser(),
fetchPosts()
]);
} catch (error) {
console.error(error);
}
}
Optional Chaining and Nullish Coalescing
// Optional chaining (?.)
let user = {
name: "Alice",
address: {
city: "New York"
}
};
// Safe property access
let city = user?.address?.city; // "New York"
let zip = user?.address?.zipCode; // undefined (no error!)
let phone = user?.contact?.phone; // undefined
// Nullish coalescing (??)
let username = user.username ?? "Guest"; // "Guest"
let count = user.count ?? 0; // 0 even if count is 0
// vs OR operator
let count2 = user.count || 0; // 0 (treats 0 as falsy)
Array Methods (ES6+)
// Array.from() - Create array from iterable
let str = "Hello";
let chars = Array.from(str); // ["H", "e", "l", "l", "o"]
// Array.of() - Create array from arguments
let arr = Array.of(1, 2, 3); // [1, 2, 3]
// find() and findIndex()
let numbers = [1, 2, 3, 4, 5];
let found = numbers.find(n => n > 3); // 4
let index = numbers.findIndex(n => n > 3); // 3
// includes()
console.log(numbers.includes(3)); // true
// flat() - Flatten nested arrays
let nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6] (depth 2)
// flatMap() - Map then flatten
let words = ["Hello World", "Goodbye Moon"];
let allWords = words.flatMap(phrase => phrase.split(" "));
// ["Hello", "World", "Goodbye", "Moon"]
11. Best Practices
Code Style and Conventions
Use Meaningful Variable Names
// Bad
let x = 10;
let d = new Date();
// Good
let userAge = 10;
let currentDate = new Date();
Use const by Default, let When Needed
// Prefer const for values that don't change
const MAX_USERS = 100;
const API_URL = "https://api.example.com";
// Use let only when reassignment is needed
let counter = 0;
counter++;
// Never use var in modern code
Use Strict Equality (===)
// Bad - loose equality (type coercion)
if (value == "5") { } // true for both "5" and 5
// Good - strict equality (no coercion)
if (value === "5") { } // only true for "5"
Use Template Literals for Strings
// Bad
let message = "Hello, " + name + "! Your balance is $" + balance;
// Good
let message = `Hello, ${name}! Your balance is $${balance}`;
Use Arrow Functions for Callbacks
// Bad
numbers.map(function(n) {
return n * 2;
});
// Good
numbers.map(n => n * 2);
Use Destructuring
// Bad
function getUser(user) {
let name = user.name;
let age = user.age;
let email = user.email;
}
// Good
function getUser({ name, age, email }) {
// Use name, age, email directly
}
Error Handling
// Always handle errors in async code
async function fetchData() {
try {
let response = await fetch(url);
let data = await response.json();
return data;
} catch (error) {
console.error("Error fetching data:", error);
// Handle error appropriately
return null;
}
}
// Validate input
function divide(a, b) {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}
Performance Tips
Cache DOM Queries
// Bad - queries DOM in every iteration
for (let i = 0; i < 100; i++) {
document.querySelector("#result").textContent = i;
}
// Good - query once, cache reference
let result = document.querySelector("#result");
for (let i = 0; i < 100; i++) {
result.textContent = i;
}
Use Event Delegation
// Bad - listener on each item
document.querySelectorAll(".item").forEach(item => {
item.addEventListener("click", handleClick);
});
// Good - one listener on parent
document.querySelector("#list").addEventListener("click", (e) => {
if (e.target.classList.contains("item")) {
handleClick(e);
}
});
Debounce Expensive Operations
// Limit how often function executes
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
// Use for search input, window resize, etc.
let search = debounce(performSearch, 300);
input.addEventListener("input", search);
Security Considerations
Avoid eval() and innerHTML with User Input
// Dangerous - XSS vulnerability!
let userInput = "";
element.innerHTML = userInput;
// Safe - escapes HTML
element.textContent = userInput;
Validate and Sanitize Input
// Always validate user input
function processEmail(email) {
// Basic validation
if (!email.includes("@") || email.length < 5) {
throw new Error("Invalid email");
}
// Sanitize
email = email.trim().toLowerCase();
return email;
}
12. Practice Exercises
Test your knowledge with these hands-on exercises. Try to solve them before looking at solutions!
Exercise 1: FizzBuzz
Write a program that prints numbers from 1 to 100. For multiples of 3, print "Fizz"; for multiples of 5, print "Buzz"; for multiples of both, print "FizzBuzz".
Show Solution
for (let i = 1; i <= 100; i++) {
if (i % 3 === 0 && i % 5 === 0) {
console.log("FizzBuzz");
} else if (i % 3 === 0) {
console.log("Fizz");
} else if (i % 5 === 0) {
console.log("Buzz");
} else {
console.log(i);
}
}
Exercise 2: Palindrome Checker
Write a function that checks if a string is a palindrome (reads the same forwards and backwards). Ignore case and spaces.
Show Solution
function isPalindrome(str) {
// Remove spaces and convert to lowercase
let cleaned = str.replace(/\s+/g, '').toLowerCase();
// Compare with reversed version
let reversed = cleaned.split('').reverse().join('');
return cleaned === reversed;
}
console.log(isPalindrome("A man a plan a canal Panama")); // true
console.log(isPalindrome("hello")); // false
Exercise 3: Array Manipulation
Given an array of numbers, return a new array with only the even numbers, doubled, and sorted in descending order.
Show Solution
function processArray(numbers) {
return numbers
.filter(n => n % 2 === 0) // Keep even numbers
.map(n => n * 2) // Double them
.sort((a, b) => b - a); // Sort descending
}
console.log(processArray([1, 2, 3, 4, 5, 6])); // [12, 8, 4]
Exercise 4: To-Do List
Create a simple to-do list application with add, remove, and toggle completion functionality.
Show Solution
class TodoList {
constructor() {
this.todos = [];
this.nextId = 1;
}
add(text) {
this.todos.push({
id: this.nextId++,
text: text,
completed: false
});
}
remove(id) {
this.todos = this.todos.filter(todo => todo.id !== id);
}
toggle(id) {
let todo = this.todos.find(todo => todo.id === id);
if (todo) {
todo.completed = !todo.completed;
}
}
getAll() {
return this.todos;
}
}
// Usage
let list = new TodoList();
list.add("Learn JavaScript");
list.add("Build a project");
list.toggle(1);
console.log(list.getAll());
Exercise 5: DOM Manipulation Challenge
Create a color picker that changes the background color of a div when clicking colored buttons.
Show Solution
// HTML:
// <div id="colorBox" style="width: 200px; height: 200px;"></div>
// <button class="color-btn" data-color="red">Red</button>
// <button class="color-btn" data-color="blue">Blue</button>
// <button class="color-btn" data-color="green">Green</button>
// JavaScript:
let colorBox = document.querySelector("#colorBox");
document.querySelectorAll(".color-btn").forEach(button => {
button.addEventListener("click", () => {
let color = button.dataset.color;
colorBox.style.backgroundColor = color;
});
});
// Or with event delegation:
document.body.addEventListener("click", (e) => {
if (e.target.classList.contains("color-btn")) {
colorBox.style.backgroundColor = e.target.dataset.color;
}
});
13. Next Steps
Your JavaScript Learning Path
Level 1: You Are Here! ✓
You've completed JavaScript basics. You now understand variables, functions, objects, arrays, DOM manipulation, and events.
Level 2: Intermediate JavaScript
Deep dive into:
Closures and scope chain
Prototypes and inheritance
Asynchronous JavaScript (Promises, async/await)
Error handling and debugging
Regular expressions
Working with APIs and fetch
Level 3: Advanced Concepts
Master advanced topics:
Design patterns (Module, Observer, Factory, etc.)
Functional programming concepts
Performance optimization
Memory management
Web Workers and Service Workers
Security best practices
Level 4: Frameworks and Tools
Learn modern JavaScript ecosystem:
React, Vue, or Angular
Node.js for backend
Build tools (Webpack, Vite, esbuild)
TypeScript
Testing (Jest, Testing Library)
State management (Redux, Zustand)
Level 5: Specialization
Choose your path:
Front-End: Advanced React, animations, PWAs
Back-End: Express.js, databases, authentication
Full-Stack: Combine both with Next.js, Remix
Mobile: React Native, Ionic
Desktop: Electron
Practice Projects
Build these projects to solidify your skills:
1. Calculator
Build a functional calculator with basic operations. Focus on event handling and DOM manipulation.
2. To-Do App
Create a to-do list with add, delete, edit, and filter functionality. Use local storage to persist data.
3. Weather App
Fetch weather data from an API and display it. Learn about async/await and API integration.
4. Quiz Game
Build an interactive quiz with score tracking. Practice with objects, arrays, and conditional logic.
5. Image Gallery
Create a filterable image gallery with search. Work with arrays, filter, and DOM updates.
6. Expense Tracker
Track income and expenses with charts. Practice data manipulation and visualization.
Learning Resources
Resource
Type
Best For
MDN Web Docs
Documentation
Comprehensive reference and tutorials
JavaScript.info
Tutorial
In-depth explanations with examples
freeCodeCamp
Interactive
Hands-on practice with projects
Eloquent JavaScript
Book
Deep understanding of concepts
You Don't Know JS
Book Series
Advanced understanding
Pro Tip: The best way to learn JavaScript is by building projects. Start small, make mistakes, debug, and gradually increase complexity. Every bug you fix makes you a better developer!