// Introduction + 12 Chapters · AngelCode Labs

JavaScript
Mastery

A complete beginner-to-advanced JavaScript course. Starts from absolute zero — no experience needed. Ends with real DOM manipulation, event handling, APIs, and modern ES6+ features.

Intro + 12 Chapters Beginner → Advanced DOM & Events ES6+ Modern JS
Introduction

What Is JavaScript and Why Does It Matter?

You have already built pages with HTML and styled them with CSS. They look good — but they do not do anything. They are static. JavaScript is what makes a page come alive: buttons that respond, forms that validate, content that loads dynamically, menus that open and close. Every interactive website you have ever used runs on JavaScript.

What Is JavaScript?

JavaScript is a programming language that runs inside your browser. It is the only programming language that browsers understand natively — you do not need to install anything. When a user clicks a button, submits a form, or scrolls down a page, JavaScript is what decides what happens next.

Think of your web page as a building. HTML is the walls and doors. CSS is the paint and furniture. JavaScript is the electricity — the thing that makes the lights turn on, the lift move, and the alarm sound.

JavaScript is now also used on servers (Node.js), in mobile apps, and in databases. But we start where it belongs: in the browser, making web pages interactive.

How JavaScript Connects to Your HTML

Just like CSS, there are three ways to add JavaScript to your page. One is clearly the right choice for real projects.

Method 1 — Inline (avoid)
<!-- JavaScript directly inside an HTML attribute — avoid this -->
<button onclick="alert('Hello!')">Click me</button>
⚠ AVOID INLINE JAVASCRIPT

Inline JavaScript is hard to maintain, cannot be reused, and mixes behaviour with structure. The same problems as inline CSS. Use it only for quick tests.

Method 2 — Internal <script> block (good for learning)
<!DOCTYPE html>
<html>
<head>
  <title>My Page</title>
</head>
<body>

  <h1>Hello World</h1>

  <!-- JavaScript goes at the END of body, before </body> -->
  <script>
    const heading = document.querySelector('h1');
    heading.textContent = 'Hello from JavaScript!';
  </script>

</body>
</html>
Method 3 — External .js file (best for real projects)
<!-- In your HTML file: load the script with defer -->
<script src="app.js" defer></script>

/* Inside app.js — your JavaScript lives here */
const btn = document.querySelector('#my-button');
btn.addEventListener('click', () => {
    alert('Button clicked!');
});
MethodWhen to use it
Inline (onclick="...")Almost never. Only for extremely quick prototype tests. Hard to maintain, cannot be reused.
<script> in bodyGood for small pages, learning, and single-file projects. Place it at the end of <body> so the HTML loads first.
<script src defer>The right way for real projects. The defer attribute makes JavaScript load in the background and run after the HTML is fully parsed. Always use this.

Your First JavaScript Program

Open the playground and type this. It is a real, working program. We will build on it throughout the course.

Your first JavaScript — try this in the playground now
// This is a comment. The browser ignores it. Write notes here.

// 1. Display a message in the browser console
console.log('Hello from JavaScript!');

// 2. Do some maths
const price    = 120;
const discount = 0.20;
const total    = price - (price * discount);

console.log('Price after 20% discount: ₵' + total);
// Output: Price after 20% discount: ₵96

// 3. Show an alert to the user
alert('Welcome to AngelCode Labs!');
✅ THE DEVELOPER CONSOLE

In any browser, press F12 (or Ctrl+Shift+I on Windows, Cmd+Option+I on Mac) to open DevTools. The Console tab shows your console.log() output. It is your best friend for testing and debugging JavaScript. Use it constantly.

Chapter 01

Variables

A variable is a container that stores a value. It is one of the most fundamental concepts in programming. Without variables, you could not remember a user's name, track a score, or save anything between lines of code. In JavaScript, there are three ways to create variables — and understanding the difference is critical.

Explanation

JavaScript has three keywords for creating variables: var, let, and const. They were not all introduced at the same time. var is the old way (before 2015). let and const are modern and should be used in all new code.

A simple rule: always start with const. Switch to let only if you need to change the value.

Code Example

Declaring and using variables — a student grade calculator
// ── const: values that will NOT change ──────────────────
const studentName  = 'Ama Owusu';
const maxScore     = 100;
const passMark     = 50;

// ── let: values that WILL change ────────────────────────
let score         = 0;     // starts at 0
let hasSubmitted  = false; // will flip to true

// ── Updating a let variable ──────────────────────────────
score = 78;           // reassign: works fine
hasSubmitted = true; // reassign: works fine

// ── Trying to reassign a const: ERROR ───────────────────
// maxScore = 120;  ← TypeError: Assignment to constant variable

// ── Using variables in expressions ──────────────────────
const percentage = (score / maxScore) * 100;
const passed     = score >= passMark;

console.log(studentName + ' scored ' + percentage + '%');
console.log('Passed: ' + passed);
// Output: Ama Owusu scored 78%
// Output: Passed: true

Code Explanation

Line / KeywordWhat it does and why it matters
const studentName = ...Creates a variable called studentName that holds the string 'Ama Owusu'. Using const means this name will never be changed. If someone accidentally tries to reassign it, JavaScript will throw an error — which is a good thing. It protects your data.
let score = 0Creates a variable called score with starting value 0. Using let signals that this value will change later in the program. That is exactly what happens on line score = 78.
score = 78Reassigns the value of score from 0 to 78. Notice: no keyword is used here — you only write const or let when first creating the variable. After that, just use the name.
(score / maxScore) * 100Variables are used in maths expressions just like numbers. JavaScript evaluates this and puts the result into percentage. This is called an expression.
score >= passMarkA comparison. If score (78) is greater than or equal to passMark (50), this evaluates to true. Otherwise false. The result is stored in passed.
console.log()Prints a message to the browser's Developer Console. This is your primary tool for seeing what is happening inside your code while you are building and testing.

What You Will See

Browser console showing the output: Ama Owusu scored 78% and Passed: true — result of the Chapter 1 variables code example
💡 NAMING VARIABLES

Variable names in JavaScript use camelCase — the first word is lowercase, and every following word starts with a capital letter. Examples: studentName, totalPrice, hasSubmitted. Names cannot start with a number or contain spaces.

Chapter 02

Data Types

Not all data is the same. The number 42 is different from the text "42". A yes/no flag is different from both. JavaScript has different types for different kinds of data. Understanding data types is what allows you to work with information correctly — and it prevents some of the most common beginner bugs.

Explanation

JavaScript has six primitive data types that you will use every day:

There is also one compound type you use constantly: Object. Arrays are a special kind of object. We cover both in later chapters.

Code Example

All six primitive types — a product listing example
// ── String — text, always in quotes ─────────────────────
const productName = 'Leather Bag - Kukurantumi Edition';
const currency    = 'GHS';         // single quotes
const brand       = "Playman's";    // double quotes (when text has apostrophe)

// ── Number — whole numbers and decimals ──────────────────
const price       = 450.00;
const stockCount  = 24;
const discount    = 0.15;   // 15% as a decimal

// ── Boolean — true or false ONLY ────────────────────────
const inStock     = true;
const isFeatured  = false;
const hasDiscount = discount > 0; // evaluates to true

// ── undefined — declared but no value assigned yet ───────
let customerReview;         // = undefined (no value given)
console.log(customerReview); // undefined

// ── null — deliberately empty ────────────────────────────
let selectedColour = null;   // no colour chosen yet — intentional

// ── typeof: check what type a value is ──────────────────
console.log(typeof productName); // "string"
console.log(typeof price);       // "number"
console.log(typeof inStock);     // "boolean"
console.log(typeof customerReview); // "undefined"
console.log(typeof null);      // "object" ← famous JS quirk! null is NOT an object

Code Explanation

Type / ConceptWhat it does and when to use it
StringAny text must be wrapped in quotes. You can use single quotes 'hello', double quotes "hello", or backticks `hello`. Backticks are special — they let you embed variables directly in the text. We cover this in the Strings chapter.
NumberJavaScript has only one number type for both whole numbers and decimals. There is no separate "int" and "float" like in other languages. 42 and 42.0 are the same thing.
BooleanBooleans are the result of comparisons and are used heavily in if statements and logic. discount > 0 produces true because 0.15 is greater than 0. There are no other boolean values — only true and false.
undefinedWhen you declare a variable with let but do not assign a value, JavaScript automatically gives it the value undefined. This means "this variable exists but has no value yet". You rarely set something to undefined deliberately.
nullnull means "intentionally empty". You set something to null when you want to signal that there is no value on purpose — for example, nothing is selected yet. The famous typeof null === "object" is a 30-year-old bug in JavaScript that was never fixed to avoid breaking old websites.
typeofThe typeof operator tells you the data type of any value. Use it when debugging to check what type you are actually working with.
📌 TYPE CONVERSION

JavaScript sometimes converts types automatically. For example, "5" + 3 gives "53" (text), not 8 (number), because the + operator prefers strings. Use Number("5") to explicitly convert text to a number when you need to do maths.

Chapter 03

Operators

Operators are the symbols that let you do things with your data — add numbers, compare values, combine conditions. JavaScript has operators for maths, for comparison, and for logic. Understanding them is what allows you to write real programs that make decisions.

Explanation

There are three groups of operators you need to know well:

The most important beginner lesson: always use === (three equals) for comparison, never == (two equals). Three equals checks both value AND type. Two equals allows type coercion which causes hard-to-find bugs.

Code Example

All major operators — a shopping cart calculation
// ── Arithmetic operators ─────────────────────────────────
const itemPrice  = 120;
const quantity   = 3;
const shipping   = 15;

const subtotal   = itemPrice * quantity; // 360  (multiply)
const total      = subtotal + shipping;  // 375  (add)
const savings    = 500 - total;         // 125  (subtract)
const perItem    = subtotal / quantity;  // 120  (divide)
const remainder  = 7 % 3;               // 1    (modulo: remainder of 7 ÷ 3)

// ── Shorthand assignment operators ───────────────────────
let cartCount = 0;
cartCount++;        // cartCount is now 1  (increment by 1)
cartCount += 2;     // cartCount is now 3  (add 2 and reassign)
cartCount -= 1;     // cartCount is now 2  (subtract 1)

// ── Comparison operators ─────────────────────────────────
console.log(total === 375);   // true  (strict equal: value AND type match)
console.log(total !== 400);   // true  (strict not equal)
console.log(total > 300);     // true  (greater than)
console.log(total <= 375);    // true  (less than or equal)
console.log(5 == '5');        // true  ← DANGEROUS: == ignores type
console.log(5 === '5');       // false ← SAFE: === checks type too

// ── Logical operators ────────────────────────────────────
const hasEnoughStock = true;
const isPro          = false;
const isLoggedIn     = true;

// AND (&&): both conditions must be true
console.log(hasEnoughStock && isLoggedIn);  // true
console.log(hasEnoughStock && isPro);      // false (isPro is false)

// OR (||): at least one must be true
console.log(isPro || isLoggedIn);          // true (isLoggedIn is true)
console.log(isPro || false);              // false (both false)

// NOT (!): flips true to false and false to true
console.log(!isPro);                       // true (flips false)
console.log(!isLoggedIn);                  // false (flips true)

Code Explanation

OperatorWhat it does
+ - * /Standard arithmetic. + also joins strings: 'Hello' + ' World' gives 'Hello World'. Be careful — '5' + 3 gives '53' not 8.
% (modulo)Returns the remainder of a division. Very useful for checking if a number is even (n % 2 === 0) or for cycling through values in a loop.
++ and --Increment (add 1) or decrement (subtract 1) a variable. count++ is shorthand for count = count + 1. Used heavily in loops.
+= -= *= /=Shorthand assignment. x += 5 means "add 5 to x and save the result back in x". Much faster to write than x = x + 5.
=== and !==Strict equality and strict inequality. Always use these. They check both value and type. 5 === '5' is false because a number and a string are not the same type.
== (avoid)Loose equality. JavaScript will convert types before comparing. 0 == false returns true, "" == false returns true. These surprises cause bugs. Use === instead.
&& (AND)Returns true only if BOTH sides are true. If the first condition is false, JavaScript does not even check the second — this is called short-circuit evaluation.
|| (OR)Returns true if AT LEAST ONE side is true. Also used as a fallback: const name = input || 'Guest' — if input is empty (falsy), use 'Guest'.
! (NOT)Flips a boolean. !true gives false. !false gives true. Used in conditions: if (!isLoggedIn) means "if the user is NOT logged in".
Chapter 04

Control Flow

Control flow is how you make your program make decisions. Without it, your code would run the same way every time, regardless of the situation. With if/else statements and loops, your code can respond to different inputs, conditions, and data — which is what makes programs actually useful.

Explanation

There are two main tools for controlling the flow of your program:

There is also the ternary operator — a compact way to write a simple if/else on one line. And switch statements — a cleaner way to handle many specific cases.

Code Example

if/else, loops, ternary, and switch — a school grade system
// ── if / else if / else ──────────────────────────────────
const score = 73;

if (score >= 80) {
    console.log('Grade: A — Excellent!');
} else if (score >= 70) {
    console.log('Grade: B — Good work!');   // ← this runs (73 >= 70)
} else if (score >= 60) {
    console.log('Grade: C — Keep going!');
} else {
    console.log('Grade: F — Please revise.');
}

// ── Ternary operator: shorthand if/else on one line ──────
const result = score >= 60 ? 'PASS' : 'FAIL';
//                condition   ↑ if true   ↑ if false
console.log(result); // 'PASS'

// ── switch: clean multiple cases ────────────────────────
const day = 'Monday';

switch (day) {
    case 'Saturday':
    case 'Sunday':
        console.log('Weekend — school closed');
        break;
    case 'Monday':
        console.log('Start of the week!'); // ← this runs
        break;
    default:
        console.log('Regular school day');
}

// ── for loop: repeat a set number of times ───────────────
for (let i = 1; i <= 5; i++) {
    console.log('Student #' + i);
}
// Output: Student #1, Student #2, Student #3, Student #4, Student #5

// ── for...of loop: iterate over an array ─────────────────
const subjects = ['Maths', 'English', 'Science', 'ICT'];
for (const subject of subjects) {
    console.log('Studying: ' + subject);
}

// ── while loop: repeat until condition is false ──────────
let attempts = 0;
while (attempts < 3) {
    console.log('Attempt ' + (attempts + 1));
    attempts++;
}
// Output: Attempt 1, Attempt 2, Attempt 3

Code Explanation

ConceptWhat it does and when to use it
if (condition) { }The code inside the curly braces { } only runs if the condition is true. If it is false, JavaScript skips that block entirely.
else if (condition)Checked only if the previous if was false. You can chain as many else if blocks as you need. JavaScript stops checking as soon as it finds the first true condition.
else { }The fallback — runs if none of the above conditions were true. Optional, but good practice to include.
Ternary ? :A one-line if/else. The format is condition ? valueIfTrue : valueIfFalse. Use it for simple, short decisions. Avoid nesting ternaries — they become unreadable.
switch (value)Compares one value against many possible cases. Cleaner than writing many else if blocks. Always include break at the end of each case — without it, JavaScript "falls through" to the next case.
for (let i = 0; ...)The classic counting loop. Three parts: initialise (let i = 0), condition (i < 5), increment (i++). Runs while the condition is true.
for...of arrayThe modern way to loop through each item in an array. Much cleaner than a counting loop when you need every element. Use const for the loop variable since each item does not change.
while (condition)Keeps running as long as the condition is true. Be careful — if you forget to increment a counter or update the condition, it loops forever (infinite loop) and freezes the browser tab.
💡 BREAK AND CONTINUE

Inside any loop, break stops the loop immediately. continue skips the current iteration and moves to the next one. Use them to control loop behaviour precisely.

Chapter 05

Functions

A function is a reusable block of code that does a specific job. Instead of writing the same code over and over, you write it once inside a function and call it whenever you need it. Functions are the foundation of all real programming — everything you build will use them constantly.

Explanation

There are a few ways to write functions in JavaScript. They all do the same thing but have slightly different syntax and behaviour:

Code Example

Functions in practice — a price calculator module
// ── Function declaration ─────────────────────────────────
function greet(name) {
    console.log('Welcome, ' + name + '!');
}
greet('Kofi');    // Welcome, Kofi!
greet('Abena');   // Welcome, Abena!

// ── Function with return value ───────────────────────────
function calculateTotal(price, quantity) {
    const subtotal = price * quantity;
    return subtotal; // send the result back to the caller
}

const orderTotal = calculateTotal(45, 3); // 135
console.log('Total: ₵' + orderTotal);

// ── Arrow function: shorter syntax ───────────────────────
const addTax = (amount) => {
    return amount * 1.15; // 15% tax
};

// Even shorter: single expression, implicit return
const addTax2 = (amount) => amount * 1.15; // same thing, one line

console.log(addTax(100));  // 115

// ── Default parameters ───────────────────────────────────
const applyDiscount = (price, discount = 0.10) => {
    return price - (price * discount);
};
console.log(applyDiscount(200));       // 180 (10% default discount)
console.log(applyDiscount(200, 0.20)); // 160 (20% custom discount)

// ── Functions calling functions ──────────────────────────
const getFinalPrice = (price, qty, discount) => {
    const discounted = applyDiscount(price, discount);
    const total      = calculateTotal(discounted, qty);
    return addTax(total);
};
console.log(getFinalPrice(100, 2, 0.15)); // 195.5

Code Explanation

ConceptWhat it does and why it matters
function name(params) { }Declares a named function. The name should describe what it does — use a verb: calculateTotal, validateEmail, getUserById. The code inside only runs when you call the function by name.
greet('Kofi')Calling the function. 'Kofi' is the argument — it is passed into the function and assigned to the parameter name. You can call the same function with different arguments and get different results.
return valueSends a result back to whoever called the function. Without return, the function returns undefined. After a return statement, the function stops — no code below it runs.
const fn = (param) => { }Arrow function syntax. Assign it to a const. The => symbol (fat arrow) separates the parameters from the function body. Cleaner and shorter than the function keyword.
(amount) => amount * 1.15If the function body is a single expression, you can skip the { } and return. The expression is automatically returned. Only use this shorthand when the function is simple enough to read in one line.
discount = 0.10Default parameter. If no discount is passed when calling the function, it uses 0.10 (10%) automatically. This prevents errors and makes functions more flexible.
✅ ONE JOB PER FUNCTION

Each function should do exactly one thing and do it well. applyDiscount() only applies a discount. addTax() only adds tax. When functions are small and focused, they are easy to test, reuse, and understand.

Chapter 06

Arrays

An array is a list of items stored in a single variable. Instead of creating separate variables for 20 student names, you put them all in one array. Arrays are one of the most used data structures in JavaScript — you will work with them constantly when displaying lists, processing data from a server, and building any kind of dynamic content.

Explanation

An array is created using square brackets []. Each item is separated by a comma. Items are accessed using their position (index) — and in JavaScript, indexes start at 0, not 1. The first item is at index 0, the second at index 1, and so on.

JavaScript gives you many built-in methods for working with arrays. The most important ones are:

Code Example

Arrays in practice — a student marks system
// ── Creating an array ────────────────────────────────────
const students = ['Ama', 'Kofi', 'Abena', 'Kwame', 'Efua'];
const marks    = [78, 62, 91, 55, 84];

// ── Accessing items (index starts at 0) ──────────────────
console.log(students[0]); // 'Ama'   (first item)
console.log(students[2]); // 'Abena' (third item)
console.log(marks.length); // 5       (number of items)

// ── Adding and removing items ────────────────────────────
students.push('Nana');   // add to the end → ['Ama','Kofi','Abena','Kwame','Efua','Nana']
students.pop();          // remove from end → removes 'Nana'

// ── map: transform each item into something new ──────────
const upperNames = students.map((name) => name.toUpperCase());
console.log(upperNames); // ['AMA', 'KOFI', 'ABENA', 'KWAME', 'EFUA']

const grades = marks.map((mark) => {
    if (mark >= 80) return 'A';
    if (mark >= 70) return 'B';
    if (mark >= 60) return 'C';
    return 'F';
});
console.log(grades); // ['B', 'C', 'A', 'F', 'A']

// ── filter: keep only items that pass a test ─────────────
const passing = marks.filter((mark) => mark >= 60);
console.log(passing); // [78, 62, 91, 84] — 55 is removed

// ── find: get the first item that matches ────────────────
const firstHigh = marks.find((mark) => mark > 80);
console.log(firstHigh); // 91

// ── forEach: loop through and do something ───────────────
students.forEach((student, index) => {
    console.log(student + ': ' + marks[index] + ' marks');
});
// Ama: 78 marks
// Kofi: 62 marks
// ... and so on

// ── includes: check if item exists ───────────────────────
console.log(students.includes('Kofi'));   // true
console.log(students.includes('Yaw'));    // false

Code Explanation

MethodWhat it does
array[0]Accesses the item at index 0 (the first item). Indexes always start at zero in JavaScript. array[array.length - 1] gives you the last item.
array.lengthReturns the number of items in the array. Not a method — it is a property (no parentheses needed).
.push(item)Adds one or more items to the end of the array. Modifies the original array. Returns the new length.
.pop()Removes and returns the last item. Modifies the original array. No argument needed.
.map(fn)Creates a NEW array by running a function on every item. The original array is unchanged. This is one of the most used array methods in all JavaScript — vital for rendering lists on screen.
.filter(fn)Creates a NEW array containing only items for which the function returns true. Use it whenever you need a subset of data — e.g. only passing students, only products in stock.
.find(fn)Returns the first item where the function returns true. Returns undefined if no match is found. Useful for looking up a specific item by ID or name.
.forEach(fn)Runs a function on every item for its side effects — like logging, updating the DOM, or sending data. Does NOT return a new array. Use map when you need a result, forEach when you just need to do something.
.includes(value)Returns true if the value exists in the array. Quick way to check membership.
Chapter 07

Objects

An object is a collection of related data and behaviour. Where an array stores a list of items, an object stores a structured set of named values. A user profile, a product, an order — all of these are naturally represented as objects in JavaScript. They are the core data structure behind every real application.

Explanation

An object is written using curly braces { }. Inside, you have key: value pairs, separated by commas. The key is the name, the value is the data. Keys are also called properties.

Objects can also contain methods — functions that belong to the object. When a function is a property of an object, it is called a method. This is what makes objects so powerful: they bundle both data and the actions that work on that data together.

Code Example

Objects in practice — a product catalogue system
// ── Creating an object ───────────────────────────────────
const product = {
    name:      'Kente Fabric',
    price:     85.00,
    currency:  'GHS',
    inStock:   true,
    category:  'Fabric',
    // A method: a function inside the object
    getLabel: function() {
        return this.name + ' — ' + this.currency + this.price;
    }
};

// ── Accessing properties ─────────────────────────────────
console.log(product.name);         // 'Kente Fabric'  (dot notation)
console.log(product['price']);    // 85  (bracket notation — used when key is dynamic)
console.log(product.getLabel());   // 'Kente Fabric — GHS85'

// ── Modifying properties ─────────────────────────────────
product.price   = 90.00;           // update existing property
product.discount = 0.10;           // add new property
delete product.category;           // remove a property

// ── Nested objects ───────────────────────────────────────
const customer = {
    name: 'Kwame Asante',
    contact: {
        email: 'kwame@email.com',
        phone: '0241234567'
    },
    orders: [1042, 1051, 1089]  // array inside an object
};
console.log(customer.contact.email); // 'kwame@email.com'
console.log(customer.orders[0]);    // 1042

// ── Array of objects — the most common pattern ───────────
const products = [
    { id: 1, name: 'Kente', price: 85 },
    { id: 2, name: 'Batik',  price: 60 },
    { id: 3, name: 'Adinkra', price: 120 },
];

// Get just the names of all products
const names = products.map((p) => p.name);
console.log(names); // ['Kente', 'Batik', 'Adinkra']

// Find a specific product by id
const found = products.find((p) => p.id === 2);
console.log(found); // { id: 2, name: 'Batik', price: 60 }

Code Explanation

ConceptWhat it does and why it matters
{ key: value }Object literal syntax. Each key is the property name, each value is the data. Keys do not need quotes (unless they contain spaces or special characters).
object.propertyDot notation — the standard way to access properties. Use this whenever you know the property name in advance.
object['property']Bracket notation — used when the property name is stored in a variable, or when the key has special characters. Example: obj[dynamicKey] where dynamicKey is a variable holding the key name.
thisInside an object method, this refers to the object itself. In getLabel(), this.name gives you the product's name. It is how a method accesses the object's own data.
Nested objectsObjects can contain other objects. Access nested values by chaining dot notation: customer.contact.email. This mirrors how real data is structured — a customer has contact details, which has an email.
Array of objectsThe most common pattern in all JavaScript development. Data from a server always arrives as an array of objects. Every product listing, user table, and news feed is an array of objects at its core. Combine this with map and filter from Chapter 6 to process data.
delete object.keyRemoves a property from an object. The property no longer exists and accessing it returns undefined.
Chapter 08

DOM Manipulation

The DOM (Document Object Model) is how JavaScript sees and interacts with your HTML page. When a browser loads an HTML file, it builds a tree of objects — one object for each HTML element. JavaScript can then reach into that tree, find any element, change its content, change its style, add new elements, or remove existing ones. This is what makes pages dynamic and interactive.

Explanation

The DOM is accessed through the global document object, which is always available in the browser. Think of document as the entry point to your entire HTML page.

The three most important things you do with the DOM:

Code Example

DOM manipulation — a dynamic product page
<!-- HTML to pair with this JavaScript -->
<h1 id="page-title">Welcome</h1>
<p class="intro">Loading...</p>
<ul id="product-list"></ul>
<button id="load-btn">Load Products</button>

/* ── JavaScript ── */

// ── Selecting elements ───────────────────────────────────
const title   = document.querySelector('#page-title');   // by ID
const intro   = document.querySelector('.intro');        // by class
const list    = document.querySelector('#product-list'); // by ID
const btn     = document.getElementById('load-btn');      // by ID (faster)

// Select multiple elements
const allItems = document.querySelectorAll('li');   // returns NodeList of all <li>

// ── Changing text content ────────────────────────────────
title.textContent = 'Product Catalogue';
intro.textContent = 'Browse our handcrafted products from Ghana.';

// ── Changing styles ──────────────────────────────────────
title.style.color    = '#f0c040';    // inline style
title.style.fontSize = '2rem';

// ── Changing CSS classes ─────────────────────────────────
intro.classList.add('active');        // add a class
intro.classList.remove('loading');   // remove a class
intro.classList.toggle('visible');   // add if absent, remove if present
intro.classList.contains('active');  // → true

// ── Creating and inserting new elements ──────────────────
const products = ['Kente Fabric', 'Batik Cloth', 'Adinkra Print'];

products.forEach((productName) => {
    const li          = document.createElement('li');   // create <li>
    li.textContent    = productName;                    // set its text
    li.className      = 'product-item';                 // set its class
    list.appendChild(li);                               // add to the <ul>
});

// ── Changing HTML content ────────────────────────────────
// Use innerHTML only for trusted content — never for user input!
btn.setAttribute('disabled', '');           // disable the button
btn.textContent = 'Products Loaded ✓';       // update its text

Code Explanation

Method / PropertyWhat it does and why it matters
document.querySelector()Selects the FIRST element that matches the CSS selector. Uses the exact same selector syntax as CSS: '#id' for IDs, '.class' for classes, 'p' for tag names. Returns the element or null if not found.
document.querySelectorAll()Returns ALL matching elements as a NodeList. Loop through it with forEach. Unlike querySelector, this never returns null — it returns an empty list if nothing matches.
getElementById()Selects an element by its ID attribute directly. The fastest selector in the DOM. Does not need the # prefix unlike querySelector.
el.textContentGets or sets the text inside an element. Does NOT interpret HTML tags — a safe way to display user-provided text without security risks.
el.style.propertySets an inline CSS style directly on an element. The CSS property name uses camelCase: fontSize not font-size, backgroundColor not background-color. Generally, it is better to add/remove CSS classes instead of setting styles directly.
classList.add/remove/toggleThe preferred way to change an element's appearance with JavaScript. You define the visual styles in CSS, then use JavaScript to switch classes on and off. This keeps style and logic separated.
document.createElement()Creates a new HTML element in memory — it is not on the page yet. Set its content and classes, then insert it into the DOM using appendChild, append, or prepend.
parent.appendChild(child)Inserts child as the last child of parent. This is how you dynamically add new content to the page — create an element, configure it, then append it to the right parent.
⚠ NEVER USE innerHTML WITH USER DATA

el.innerHTML = userInput is a serious security vulnerability called XSS (Cross-Site Scripting). A malicious user could inject JavaScript into your page. Always use textContent for text, or build elements using createElement.

Chapter 09

Events

An event is anything that happens on a web page — a click, a key press, a form submission, the page loading, the user scrolling. Events are how your JavaScript code knows when to run. Without events, your code would have no way to respond to what the user is doing. Every interactive web application is built on event handling.

Explanation

You attach an event listener to an element using addEventListener(). When that event happens on that element, your function (the event handler) runs automatically.

Every event handler receives an event object — usually named e or event — which contains information about what happened: which element was clicked, what key was pressed, what position the mouse was at. This object is your primary source of information inside an event handler.

Code Example

Event handling — an interactive search and form
<!-- HTML -->
<button id="like-btn">♡ Like</button>
<input id="search" type="text" placeholder="Search products...">
<form id="contact-form">...</form>

/* ── JavaScript ── */

// ── Click event ──────────────────────────────────────────
const likeBtn = document.querySelector('#like-btn');
let liked     = false;
let likeCount = 0;

likeBtn.addEventListener('click', (e) => {
    liked      = !liked;   // toggle true/false
    likeCount += liked ? 1 : -1;
    e.target.textContent = liked ? '♥ Liked (' + likeCount + ')' : '♡ Like';
    e.target.classList.toggle('liked');
});

// ── Input event: react as user types ────────────────────
const searchBox = document.querySelector('#search');

searchBox.addEventListener('input', (e) => {
    const query = e.target.value.toLowerCase();
    console.log('Searching for: ' + query);
    // You would filter products here (see Chapter 6)
});

// ── Keyboard event ───────────────────────────────────────
searchBox.addEventListener('keydown', (e) => {
    if (e.key === 'Enter') {
        console.log('Search submitted: ' + e.target.value);
    }
    if (e.key === 'Escape') {
        e.target.value = '';  // clear the input
    }
});

// ── Form submit event ────────────────────────────────────
const form = document.querySelector('#contact-form');

form.addEventListener('submit', (e) => {
    e.preventDefault(); // STOP the page from refreshing (default form behaviour)

    const name    = document.querySelector('#name-input').value;
    const message = document.querySelector('#message-input').value;

    if (!name || !message) {
        alert('Please fill in all fields.');
        return;
    }
    console.log('Form submitted by ' + name);
    // Send data to server here (see Chapter 11 — Async)
});

Code Explanation

ConceptWhat it does and why it matters
addEventListener('event', fn)Attaches a listener to an element. When the named event occurs, the function runs. You can add multiple listeners to the same element. This is the correct way to handle events — never use onclick="" in HTML.
The event object (e)Automatically passed to your handler function. Contains everything about the event: which element fired it, what key was pressed, mouse position, and more. Name it e, evt, or event — it is a convention, not a rule.
e.targetThe specific element the event happened on. If you have a listener on a button and the user clicks it, e.target is that button. Very useful for reading the element's value or toggling its class.
e.target.valueFor inputs, this is the current text the user has typed. Used in input and change events to read what the user has entered.
e.keyFor keyboard events, this is the key that was pressed. 'Enter', 'Escape', 'ArrowUp', 'a', etc.
e.preventDefault()Stops the browser's default action for the event. On a form's submit event, the default is to reload the page. On a link's click event, the default is to navigate. Call this when you want to handle the action yourself with JavaScript instead.
'click' eventFires when an element is clicked. Works on any element, but should semantically be used on buttons and links.
'input' eventFires every time the value of an input changes — as the user types, pastes, or deletes. Use this for live search and real-time validation.
'submit' eventFires when a form is submitted. Attach this to the <form> element, not the submit button. Always call e.preventDefault() to prevent the default page reload.
Chapter 10

Strings & Template Literals

Strings are the most common data type you will work with as a web developer. Names, messages, URLs, HTML content, search queries — all text. JavaScript comes with a rich set of string methods that let you search, transform, split, and build text. Master these and working with text becomes effortless.

Explanation

The most important string feature in modern JavaScript is the template literal — a string written with backtick characters (`) instead of quotes. Template literals let you embed variables and expressions directly inside a string using ${}. This is far cleaner than using the + operator to join strings.

Beyond that, JavaScript provides many built-in string methods. The ones you will use most often:

Code Example

Template literals and string methods — a message generator
// ── Template literals: backtick strings ─────────────────
const name    = 'Ama Serwaa';
const score   = 87;
const subject = 'Mathematics';

// Old way (messy with +):
const oldMsg = 'Hello ' + name + '! You scored ' + score + ' in ' + subject + '.';

// New way (clean with template literal):
const message = `Hello ${name}! You scored ${score} in ${subject}.`;
console.log(message); // Hello Ama Serwaa! You scored 87 in Mathematics.

// Expressions inside ${}:
const grade = `Grade: ${score >= 80 ? 'A' : 'B'}`;
console.log(grade); // Grade: A

// Multi-line strings (template literals support line breaks):
const report = `
Student: ${name}
Score:   ${score}/100
Subject: ${subject}
Grade:   ${score >= 80 ? 'A' : 'B'}
`;
console.log(report);

// ── String methods ───────────────────────────────────────
const raw = '  Hello, World!  ';

raw.trim()                    // 'Hello, World!'    remove spaces
raw.toLowerCase()             // '  hello, world!  '
raw.toUpperCase()             // '  HELLO, WORLD!  '
raw.includes('World')         // true
raw.startsWith('  Hello')      // true
raw.replace('World', 'Ghana') // '  Hello, Ghana!  '
raw.slice(2, 7)               // 'Hello'  (from index 2, up to but not including 7)
raw.split(', ')               // ['  Hello', 'World!  ']
raw.trim().length              // 13  (you can chain methods)

// ── Number to string and back ────────────────────────────
const numStr  = '42';
const asNum   = Number(numStr);        // 42    (string → number)
const backStr = 42.toString();          // '42'  (number → string)
const price   = (45.678).toFixed(2);    // '45.68' (2 decimal places, returns string)

Code Explanation

Method / SyntaxWhat it does
`text ${variable}`Template literal. Backtick string with embedded expressions using ${}. The expression can be a variable, a calculation, or a ternary — anything that evaluates to a value. Use this for all string building in modern JavaScript.
.trim()Removes whitespace from both ends of the string. Essential when working with user input — users often accidentally add spaces when typing in forms. Always trim before validating or comparing strings.
.toLowerCase() / .toUpperCase()Convert the entire string to lower or upper case. Use toLowerCase() when comparing strings — it prevents "Ghana" and "ghana" from being treated as different.
.includes(text)Returns true if the string contains the given text anywhere inside it. Case-sensitive. Use with toLowerCase() for case-insensitive search: name.toLowerCase().includes(query).
.replace(old, new)Replaces the first occurrence of a substring with a new value. To replace all occurrences, use replaceAll(old, new) (ES2021).
.split(separator)Splits the string into an array of pieces wherever the separator appears. 'a,b,c'.split(',') gives ['a','b','c']. Useful for parsing CSV data, breaking up email addresses, processing URLs.
.slice(start, end)Extracts a portion of the string from index start up to (but not including) end. Negative numbers count from the end: str.slice(-3) gets the last 3 characters.
Number(str)Converts a string to a number. If the string cannot be converted (e.g. "hello"), the result is NaN (Not a Number). Always check for NaN when converting user input: isNaN(Number(input)).
.toFixed(n)Formats a number with exactly n decimal places and returns a string. Use this for displaying prices and percentages.
Chapter 11

Async JavaScript & Fetch

Most real web applications need to communicate with a server — to load products, send a form, check login credentials, or retrieve data. This communication takes time. Async JavaScript is how you write code that waits for that response without freezing the entire page. Fetch is the built-in browser tool for making HTTP requests.

Explanation

JavaScript runs one instruction at a time. But some tasks take time — loading data from a server, reading a file, waiting for a timer. If JavaScript stopped and waited for each one, the page would freeze. Async JavaScript lets you start a task and then continue with other code — when the task finishes, your callback or await expression resumes.

There are three stages of async JavaScript. You need to understand all three because you will see them all in real code:

Code Example

Fetch API with async/await — loading products from a server
// ── The Fetch API: make HTTP requests ────────────────────
// fetch() returns a Promise. We use async/await to handle it.

async function loadProducts() {
    try {
        // 1. Make the request — 'await' pauses here until the response arrives
        const response = await fetch('https://api.example.com/products');

        // 2. Check if the request was successful
        if (!response.ok) {
            throw new Error('Failed to load products: ' + response.status);
        }

        // 3. Parse the response body as JSON — also async
        const products = await response.json();

        // 4. Use the data (products is now a JavaScript array/object)
        console.log(products); // [{id:1, name:'Kente', price:85}, ...]
        displayProducts(products);

    } catch (error) {
        // If ANYTHING above fails, execution jumps here
        console.error('Error: ' + error.message);
    }
}

// Call the async function
loadProducts();

// ── Display results in the DOM ───────────────────────────
function displayProducts(products) {
    const container = document.querySelector('#product-list');
    container.innerHTML = ''; // clear existing content

    products.forEach((product) => {
        const li = document.createElement('li');
        li.textContent = `${product.name} — ₵${product.price}`;
        container.appendChild(li);
    });
}

// ── Sending data with POST ───────────────────────────────
async function submitOrder(orderData) {
    try {
        const response = await fetch('/api/orders', {
            method:  'POST',
            headers: { 'Content-Type': 'application/json' },
            body:    JSON.stringify(orderData),  // convert object to JSON text
        });

        const result = await response.json();
        console.log('Order created: #' + result.orderId);

    } catch (error) {
        console.error('Order failed: ' + error.message);
    }
}

submitOrder({ product: 'Kente', quantity: 2, customer: 'Ama' });

Code Explanation

ConceptWhat it does and why it matters
async functionDeclaring a function with async allows you to use await inside it. An async function always returns a Promise, even if you return a plain value.
awaitPauses the async function at that line until the Promise resolves. The rest of the page continues working normally — only this function pauses. Without await, you would get a Promise object instead of the actual data.
fetch(url)Makes an HTTP GET request to the URL and returns a Promise. The Promise resolves with a Response object when the server replies — not the actual data yet, just the response wrapper.
response.okA boolean that is true if the HTTP status code is 200–299 (success). Always check this — a 404 or 500 error does not throw automatically; you have to check and throw yourself.
response.json()Reads the response body and parses it as JSON. This is also async — it returns a Promise. That is why you need a second await. The result is a JavaScript object or array.
try { } catch (error) { }Wraps the async code in error handling. If anything inside try throws an error — network failure, bad JSON, server error — execution jumps to catch. Always use this with await.
method: 'POST'By default, fetch makes a GET request (read data). Use method: 'POST' to send data. Also include headers with the content type and a body containing the data.
JSON.stringify(data)Converts a JavaScript object into a JSON string — the format servers expect. The reverse is JSON.parse(text) which converts a JSON string back to a JavaScript object.
📌 WHEN TO USE ASYNC/AWAIT

Use async/await any time you use fetch(), read from a database, or use any other API that returns a Promise. It is the modern standard and it makes your code read like normal synchronous code — top to bottom, step by step.

Chapter 12

Best Practices & Clean Code

You now know JavaScript. This final chapter shows you how to write it well. There is a huge difference between code that works and code that is professional — readable, maintainable, and reliable. These habits, applied from the beginning of every project, are what separate junior developers from experienced ones.

Explanation

Professional JavaScript follows consistent patterns. Here are the most important ones:

Code Example

✗ Poor practice
// Unclear names
var x = 120;
var y = 3;
var z = x * y;

// Loose comparison (bug risk)
if (z == '360') {
    console.log('ok');
}

// Duplicated logic
function d(p) {
    return p * 0.9;
}
function d2(p) {
    return p * 0.8;
}
✓ Professional practice
// Clear, descriptive names
const unitPrice  = 120;
const quantity   = 3;
const orderTotal = unitPrice * quantity;

// Strict equality (safe)
if (orderTotal === 360) {
    console.log('Order confirmed');
}

// One reusable function
const applyDiscount = (price, rate) => {
    return price * (1 - rate);
};
// applyDiscount(price, 0.10) → 10% off
// applyDiscount(price, 0.20) → 20% off
More best practices — a complete, clean module
// ── 1. Load JS with defer — never block rendering ────────
<script src="app.js" defer></script>

// ── 2. Use strict mode at the top of each file ───────────
'use strict';

// ── 3. Guard your DOM queries ────────────────────────────
const btn = document.querySelector('#submit-btn');
if (btn) {  // always check — element may not be on every page
    btn.addEventListener('click', handleSubmit);
}

// ── 4. Keep functions small and named ────────────────────
function handleSubmit(e) {
    e.preventDefault();
    const input = getFormData();
    if (isValid(input)) {
        sendToServer(input);
    }
}
const getFormData  = () => document.querySelector('#name').value.trim();
const isValid      = (v) => v.length >= 2;

// ── 5. Always handle async errors ───────────────────────
async function sendToServer(data) {
    try {
        const res = await fetch('/api/submit', {
            method:  'POST',
            headers: { 'Content-Type': 'application/json' },
            body:    JSON.stringify({ name: data }),
        });
        if (!res.ok) throw new Error('Server error: ' + res.status);
        console.log('✓ Submitted successfully');
    } catch (err) {
        console.error('✗ Submission failed: ' + err.message);
    }
}

// ── 6. Use console.log only for development ──────────────
// Remove all console.log() calls before deploying to production.
// Use a proper error tracking tool instead (e.g. Sentry).

Code Explanation

Best PracticeWhy it matters
const by defaultForces you to think about whether a value truly needs to change. Most values in a program do not change. Using const communicates intent to other developers and prevents accidental reassignment.
Meaningful namesCode is read far more often than it is written. unitPrice tells you everything. x tells you nothing. Future-you will thank present-you for writing clear names.
'use strict'Enables strict mode — JavaScript throws errors for common mistakes that would otherwise fail silently, like using an undeclared variable. Add it to the top of every JavaScript file.
Guard DOM queriesIf you call querySelector() for an element that does not exist on the current page, it returns null. Calling addEventListener on null throws an error. Always check the result first.
Small named functionshandleSubmit reads like a sentence: get the form data, check if it is valid, send it to the server. Each step is a named function. This makes the code self-documenting and easy to test one piece at a time.
Always use try/catch with awaitNetwork requests can fail. Servers can return errors. Without error handling, your application crashes silently. Always wrap await calls in try/catch.
Remove console.log in productionConsole logs expose internal application information to anyone who opens DevTools. Clean them up before deploying. Use error tracking tools like Sentry for production monitoring.
script defer attributeAs covered in the Introduction — always load your external JavaScript file with defer. This prevents your script from blocking the HTML from rendering, which improves page load speed.

Course Complete

🏆 COURSE COMPLETE

You have now learned the full foundation of JavaScript — from variables and data types all the way to async programming, DOM manipulation, and professional best practices. The next step is to build real projects. Open the playground and start with the exercises and project for this course.

📦 DATA

Variables, data types, operators — the building blocks of every program.

🧠 LOGIC

if/else, loops, and functions — making programs think and decide.

📋 COLLECTIONS

Arrays and objects — storing, organising, and processing lists of data.

🌐 DOM

Selecting and modifying HTML — making the page interactive and dynamic.

🖱️ EVENTS

Clicks, inputs, forms — responding to what the user does.

🔌 ASYNC

Fetch, async/await — communicating with servers and handling time.