JavaScript Basics - Complete Fundamentals Course

Master the essential building blocks of JavaScript programming from variables to DOM manipulation

What You'll Learn

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

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>

3. External JavaScript File (Best Practice)

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JavaScript Demo</title> </head> <body> <h1>JavaScript Demo</h1> <!-- Load external JavaScript file --> <script src="script.js"></script> <!-- Or load from CDN --> <script src="https://cdn.example.com/library.js"></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

Development Tools

Tool Purpose Why Use It
VS Code Code Editor Free, excellent JavaScript support, extensions, debugging
Chrome DevTools Browser Debugger Inspect elements, debug code, monitor performance
Node.js JavaScript Runtime Run JavaScript outside the browser, build tools
npm Package Manager Install libraries and tools (comes with Node.js)

3. Variables and Data Types

Variable Declarations

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}!`; }

Type Checking

// typeof operator console.log(typeof 42); // "number" console.log(typeof "hello"); // "string" console.log(typeof true); // "boolean" console.log(typeof undefined); // "undefined" console.log(typeof null); // "object" (historical bug!) console.log(typeof {}); // "object" console.log(typeof []); // "object" console.log(typeof function(){}); // "function" // Check if array console.log(Array.isArray([])); // true console.log(Array.isArray({})); // false

Type Conversion

// 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

7. Objects and Arrays

Objects

Creating Objects

// Object literal (most common) let person = { firstName: "Alice", lastName: "Smith", age: 30, email: "alice@example.com", // Method fullName: function() { return `${this.firstName} ${this.lastName}`; }, // ES6 method shorthand greet() { return `Hello, I'm ${this.firstName}`; } }; // Accessing properties console.log(person.firstName); // Dot notation console.log(person["lastName"]); // Bracket notation // Dynamic property access let prop = "age"; console.log(person[prop]); // 30 // Calling methods console.log(person.fullName()); // "Alice Smith"

Object Manipulation

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!