JavaScript Exercises with Answers
Practise JavaScript with 249 interactive exercises: multiple choice, fill-in-the-blank, output prediction and debugging. Every exercise gives instant feedback, a hint and the full solution.
1JS Introduction
Read the lessonPrint the message "Ready!" to the console.
.("Ready!");
console.log() prints to the console.What does this print?
console.log(10 - 4);
console.log("10 - 4");Which is a JavaScript comment?
// for single lines, /* */ for blocks.This code should print two lines, but it throws errors. Fix it.
Console.log("Good morning!");
console.log(Have a great day);- Prints "Good morning!"
- Prints "Have a great day"
- No errors
2Where to Put JS
Read the lessonLoad app.js so it runs after the HTML is parsed.
<script ="app.js" ></script>
src points at the file; defer delays running until the HTML is ready.What will a plain script in the <head> get from document.querySelector("button") when the button is in the body?
Which attribute makes scripts run in order after parsing?
defer keeps order and waits for the HTML.The script runs before the paragraph exists, so it fails. Move the script so it can find the paragraph (don't change the JavaScript itself).
<script>
document.querySelector("#greet").textContent = "Hello from JS!";
</script>
<p id="greet">Waiting…</p>- The paragraph was updated
- No errors
3Output & Console
Read the lessonPrint a warning and an error.
console.("Battery low");
console.("Battery empty!");
console.warn for warnings, console.error for errors.What does this print?
console.log("Total: " + 4 + 6);What is the best way to show a result to users on the page?
This should print Level: 5 and show Level 5 in the paragraph. Fix it.
<p id="level"></p>const level = 5;
console.log("Level: " level);
document.querySelector("#level").textcontent = "Level " + level;- Logs "Level: 5"
- The paragraph says "Level 5"
4Variables (let, const)
Read the lessonCreate a constant for the planet and a variable for the day count, then add one day.
planet = "Mars";
days = 1;
days = days + 1;
console.log(planet, days);
planet never changes (const); days is reassigned (let).What does this print?
let x = 5;
x = x + 10;
x = x * 2;
console.log(x);Which variable name is valid and follows the convention?
What happens?
const colors = ["red"];
colors.push("blue");
console.log(colors);push changes the array's contents; the variable still points to the same array.The counter should go from 0 to 2 and print Clicks: 2. Fix the declarations.
const clicks = 0;
clicks = clicks + 1;
let clicks = clicks + 1;
console.log("Clicks:", clicks);- Logs "Clicks: 2"
- No errors
5Data Types
Read the lessonComplete the comments with each value's typeof result.
typeof "42" // ""
typeof 42 // ""
typeof true // ""
What does this print?
console.log("3" + 4 + 5);
console.log(3 + 4 + "5");What does typeof null return?
The total should be 25, but it prints 1510. Fix the code so the strings are converted to numbers.
const apples = "15";
const pears = "10";
const total = apples + pears;
console.log("Total fruit:", total);- Logs "Total fruit: 25"
- total is a number
6Operators
Read the lessonAdd 5 to the score, then check if it's at least 20.
let score = 15;
score 5;
const passed = score 20;
console.log(passed);
+= adds to the variable; >= means "greater than or equal to".What does this print?
console.log(10 % 3);
console.log(2 + 3 * 4);What does this print?
const a = 0 || "default";
const b = 0 ?? "default";
console.log(a, b);|| skips any falsy value including 0; ?? only skips null and undefined.The program should log Delivery included: true when the cart total is at least 50 and the user is a member. Fix the operators.
const cartTotal = 60;
const isMember = true;
const deliveryIncluded = cartTotal => 50 & isMember;
console.log("Delivery included:", deliveryIncluded);- Logs "Delivery included: true"
- deliveryIncluded is a boolean
>=, and logical AND is two ampersands.7Strings & Template Literals
Read the lessonBuild the greeting with a template literal.
const user = "Kai";
const msg = Welcome back, !;
console.log(msg);
${user} for the variable.What does this print?
const city = "Paris";
console.log(city.length, city[1]);Which kind of quotes makes ${5 * 3} turn into 15?
${ } placeholders.The message should read Mia has 3 new messages. Fix it using a template literal.
const person = "Mia";
const count = 3;
const message = "${person} has ${count} new messages";
console.log(message);- Logs "Mia has 3 new messages"
8String Methods
Read the lessonRemove the spaces and make it uppercase.
const code = " abc123 ";
const clean = code.().();
console.log(clean); // "ABC123"
trim() removes outer spaces; toUpperCase() capitalises.What does this print?
console.log("butterfly".slice(0, 6));What does this print?
const tags = "html, css, js".split(", ");
console.log(tags.length, tags[2]);The search should find "Pizza" when the user types "pizza", and the result should log Found: true. Fix it.
const menu = "Burger, Pizza, Salad";
const query = "pizza";
const found = menu.include(query);
console.log("Found:", found);- Logs "Found: true"
- No errors
9Numbers & Math
Read the lessonRound 7.8 down and 7.2 up.
console.log(Math.(7.8)); // 7
console.log(Math.(7.2)); // 8
floor always goes down; ceil always goes up.What does this print?
console.log(Math.round(3.5), Math.max(4, 11, 7));Which expression gives a random whole number from 1 to 10?
The average should be 82.5 and display as Average: 82.50. Fix the bugs.
const scores = ["80", "85"];
const sum = scores[0] + scores[1];
const average = sum / scores.length;
console.log("Average:", average.toFixed(2));- Logs "Average: 82.50"
+ joins them.10Booleans & Comparisons
Read the lessonUse strict comparisons.
const pin = 1234;
console.log(pin 1234); // true
console.log(pin 1111); // true
=== checks equality; !== checks inequality, both without converting types.What does this print?
console.log(Boolean("0"), Boolean(0), Boolean([]));Which of these is falsy?
The check should log Access: true only when the code typed is exactly the number 42. The input arrives as a string. Fix it so it uses strict equality correctly.
const typed = "42";
const access = typed = 42;
console.log("Access:", access);- Logs "Access: true"
- access is a boolean
- No errors
= assigns. Convert the string, then compare with ===.11Logical Operators
Read the lessonGive size a default of 12 only if fontSize is null or undefined.
const fontSize = null;
const size = fontSize 12;
console.log(size);
?? only replaces null and undefined.What is logged?
console.log(0 || "a", 0 ?? "b", "" && "c");0 || "a" gives "a", 0 ?? "b" keeps 0, and "" && "c" stops at the falsy empty string.Which condition is true only when both values are true?
What does user.settings?.theme return if settings is undefined?
describe should return "no charge" for children under 5 or adults over 65, keep a discount of 0, and not crash when pass is missing. Fix the three bugs.
function describe(age, pass) {
const discount = pass.discount || 10;
const exempt = age < 5 && age > 65;
return exempt ? "no charge" : `pay, discount ${discount}`;
}
console.log(describe(3));
console.log(describe(30, { discount: 0 }));- Young children pay nothing
- Over 65s pay nothing
- A discount of 0 is kept
- No pass gives the default
12if / else
Read the lessonComplete the condition.
const age = 20;
(age >= 18) {
console.log("Adult");
} {
console.log("Minor");
}
if (condition) { } else { }What does this print?
const x = 12;
if (x > 5) {
console.log("A");
} else if (x > 10) {
console.log("B");
} else {
console.log("C");
}Which condition is true on Saturday or Sunday?
day in both parts and join with OR.The program should print Delivery included! when the basket is £40 or more, otherwise Add £X to get delivery included. It currently has two bugs. Fix them.
const basket = 32;
if (basket > 40); {
console.log("Delivery included!");
} else {
console.log(`Add £${40 - basket} to get delivery included`);
}- Logs "Add £8 to get delivery included"
- Does not log the included message
- No errors
13switch
Read the lessonComplete the switch.
switch (pet) {
"dog":
console.log("Woof");
;
:
console.log("...");
}
case, break stops it, default is the fallback.What does this print?
const x = "2";
switch (x) {
case 2:
console.log("number");
break;
default:
console.log("no match");
}What happens if a matching case has no break?
Picking "M" should log Price: 7, but it logs Price: 9. Fix it.
const size = "M";
let price;
switch (size) {
case "S":
price = 5;
case "M":
price = 7;
case "L":
price = 9;
}
console.log("Price:", price);- Logs "Price: 7"
14for Loops
Read the lessonLog the numbers 1 to 3.
for (let i = 1; i 3; ) {
console.log(i);
}
What does this print?
for (let i = 0; i < 3; i++) {
console.log(i * 2);
}How many times does "Hi" print?
for (let i = 10; i > 0; i -= 2) {
console.log("Hi");
}This should log every guest's name in capitals, but it logs undefined at the end and crashes. Fix the loop.
const guests = ["ana", "ben", "cleo"];
for (let i = 0; i <= guests.length; i++) {
console.log(guests[i].toUpperCase());
}- Logs the three names in capitals
- No errors
15while Loops
Read the lessonCount down from 3 to 1.
let n = 3;
(n > 0) {
console.log(n);
n;
}
What does this print?
let x = 10;
while (x < 5) {
console.log("inside");
}
console.log("done");Which loop always runs its body at least once?
This loop should halve the number until it's below 1 and log Steps: 7, but it never ends. Fix it.
let value = 100;
let steps = 0;
while (value >= 1) {
steps++;
}
console.log("Steps:", steps);- Logs "Steps: 7"
- No errors
value >= 1 become false?16for...of & break
Read the lessonLoop over each colour's value.
const colours = ["red", "green", "blue"];
for (const colour colours) {
console.log(colour);
}
for...of gives values.What is logged?
for (const n of [1, 2, 3, 4, 5]) {
if (n === 2) continue;
if (n === 4) break;
console.log(n);
}What does for...in give you for an object?
Which loop can you stop early with break?
firstNegative should return the first negative number in the array, or null if there isn't one. Fix it.
function firstNegative(nums) {
let found = null;
for (const i in nums) {
if (nums[i] < 0) {
found = i;
continue;
}
}
return found;
}
console.log(firstNegative([4, -2, 7, -9]));- Returns the first negative value
- Returns null when there is none
- Works when the first item is negative
17Functions
Read the lessonComplete the function so it returns the area of a rectangle.
area(width, height) {
width * height;
}
console.log(area(3, 4)); // 12
function and send the result back with return.What does this print?
function add(a, b = 10) {
return a + b;
}
console.log(add(5), add(5, 1));What does this print?
function shout(word) {
console.log(word.toUpperCase());
}
const result = shout("hey");
console.log(result);getInitials("Grace Hopper") should return "GH". Fix the function.
function getInitials(fullName) {
const parts = fullName.split(" ");
const initials = parts[0][0] + parts[1][0];
console.log(initials);
}
console.log(getInitials("Grace Hopper"));- getInitials returns "GH"
- Works for another name
18Arrow Functions
Read the lessonTurn this into a one-line arrow function with an implicit return.
const triple = n * 3;
console.log(triple(4)); // 12
(n) => n * 3 has no braces, so the value is returned automatically.What does this print?
const greet = name => `Hi ${name}`;
const nothing = () => {};
console.log(greet("Lu"), nothing());{} here is an empty function body, so it returns undefined.How do you return an object from an arrow function in one line?
isEven should return true/false and squareAll should return the squared array. Fix both arrows.
const isEven = (n) => { n % 2 === 0 };
const squareAll = (arr) => arr.map((x) => { x * x });
console.log(isEven(4), squareAll([1, 2, 3]));- isEven(4) is true
- isEven(7) is false
- squareAll([1, 2, 3]) is [1, 4, 9]
19Scope & Closures
Read the lessonWhere can a const declared inside an if block be used?
let and const are block-scoped.What does this print?
let x = "outer";
function test() {
let x = "inner";
console.log(x);
}
test();
console.log(x);x shadows the outer one only inside the function.What does this print?
function make() {
let n = 0;
return () => n += 10;
}
const add = make();
add();
console.log(add());n between calls: 10, then 20.message is used outside the block it was declared in. Fix the code so it logs Status: Open without errors.
const hour = 10;
if (hour >= 9 && hour < 17) {
const message = "Open";
} else {
const message = "Closed";
}
console.log("Status:", message);- Logs "Status: Open"
- No errors
20Hoisting & var vs let
Read the lessonDeclare a value that will never be reassigned.
TAX_RATE = 0.2;
console.log(TAX_RATE);
const by default.What is logged?
console.log(typeof x, typeof y);
var x = 1;
function y() {}What scope does let have?
What is the temporal dead zone?
makeHandlers should return three functions that return 0, 1 and 2, and total should not leak into the global scope. Fix it.
function makeHandlers() {
const list = [];
for (var i = 0; i < 3; i++) list.push(() => i);
return list;
}
function addUp(nums) {
total = 0;
for (const n of nums) total += n;
return total;
}
console.log(makeHandlers().map(f => f()), addUp([1, 2]));- Each handler keeps its own value
- addUp still works
- total is not a global
21Callbacks
Read the lessonPass logItem as the callback to forEach without calling it.
function logItem(item) { console.log(item); }
["a", "b"].forEach();
What is logged?
function make(x) {
return (y) => x + y;
}
const add5 = make(5);
console.log(add5(2), make(1)(1));applyToAll should return a new array with fn applied to every item, and addTax should be a function returned by withTax that adds 20%. Fix both.
function applyToAll(items, fn) {
const out = [];
for (const item of items) out.push(fn);
return out;
}
function withTax(rate) {
(price) => price * (1 + rate);
}
const addTax = withTax(0.2);
console.log(applyToAll([10, 20], addTax));- withTax returns a function
- Tax is added
- applyToAll calls the callback for each item
22Recursion
Read the lessonComplete the base case so power(2, 0) returns 1.
function power(base, exp) {
if (exp === ) return 1;
return base * power(base, exp - 1);
}
console.log(power(2, 5));
What does this log?
function f(n) {
if (n === 0) return "";
return n + f(n - 1);
}
console.log(f(3));What stops a recursive function from running forever?
Which error appears when recursion goes too deep?
depth(obj) should return how deeply objects are nested: {} is 1, { a: {} } is 2. Non-object values count as 0. Fix the function.
function depth(value) {
if (typeof value === "object") return 0;
let deepest = 0;
for (const key in value) {
deepest = Math.max(deepest, depth(value));
}
return deepest;
}
console.log(depth({ a: { b: {} } }));- Empty object is 1
- Nested objects count each level
- Plain values are 0
- Takes the deepest branch
23Arrays
Read the lessonAdd "kiwi" to the end and remove the first item.
const fruit = ["apple", "pear"];
fruit.("kiwi");
fruit.();
console.log(fruit); // ["pear", "kiwi"]
push adds to the end; shift removes from the start.What does this print?
const nums = [5, 10, 15];
console.log(nums.length, nums[nums.length - 1]);What does this print?
const a = ["x"];
const b = a;
b.push("y");
console.log(a.length);b and a are the same array.The code should remove "Tuesday" from the list and log Mon, Wed, Thu. Fix it.
const days = ["Mon", "Tue", "Wed", "Thu"];
const pos = days.indexOf("Tuesday");
days.splice(pos);
console.log(days.join(", "));- Logs "Mon, Wed, Thu"
splice needs to know how many items to remove.24Array Methods (map, filter…)
Read the lessonDouble every number, then keep only those over 5.
const result = [1, 2, 3, 4]
.(n => n * 2)
.(n => n > 5);
console.log(result); // [6, 8]
map transforms each item; filter keeps items that pass the test.What does this print?
const r = [3, 5, 7].reduce((sum, n) => sum + n, 10);
console.log(r);Which method returns the first matching item (not an array)?
find returns a single item; filter returns an array.What does this print?
const ages = [12, 19, 25];
console.log(ages.some(a => a < 13), ages.every(a => a > 18));The code should log the names of users over 18 in alphabetical order: ["Ana", "Zoe"]. Fix it.
const users = [
{ name: "Zoe", age: 22 },
{ name: "Max", age: 16 },
{ name: "Ana", age: 30 },
];
const adults = users.forEach(u => u.age > 18);
const names = adults.map(u => { u.name });
console.log(names.sort());- Logs ["Ana", "Zoe"]
- No errors
25Sort & Search Arrays
Read the lessonSort the numbers from smallest to largest.
const nums = [30, 4, 15];
nums.sort((a, b) => a b);
console.log(nums);
a - b sorts numbers ascending.What is logged?
const a = [3, 1, 2];
const b = a.toSorted();
a.reverse();
console.log(a.join(""), b.join(""));Which method returns the first object that matches a condition?
Which compare function sorts names alphabetically, handling accents?
topThree(players) should return the names of the three highest scorers, highest first, without changing the original array. Fix it.
function topThree(players) {
return players.sort((a, b) => a.score > b.score).slice(0, 3).map(p => p.name);
}
const players = [
{ name: "A", score: 5 }, { name: "B", score: 50 }, { name: "C", score: 12 }, { name: "D", score: 30 },
];
console.log(topThree(players), players[0].name);- Returns the top three, highest first
- Original order is kept
26Objects
Read the lessonRead the city with dot notation, and the country using the variable key.
const place = { city: "Lagos", country: "Nigeria" };
const key = "country";
console.log(place.);
console.log(place[ ]);
place.city uses dot notation; place[key] uses the value stored in key.What does this print?
const car = { brand: "Tesla", year: 2022 };
car.year = 2024;
console.log(car.year, car.color);Which line safely reads user.profile.avatar when profile might not exist?
?. after the part that might be missing.The code should log Mia: 3 pets but crashes and prints the wrong thing. Fix it.
const owner = {
name: "Mia",
pets: ["cat", "rabbit", "fish"],
describe: () => {
return `${this.name}: ${this.pets.lenght} pets`;
},
};
console.log(owner.describe());- Logs "Mia: 3 pets"
- No errors
27Object Methods
Read the lessonGet an array of the object's keys.
const car = { make: "Mini", year: 2020 };
console.log(Object.(car));
Object.keys returns the property names.What is logged?
const a = { list: [1] };
const b = { ...a };
b.list.push(2);
console.log(a.list.length, a === b);Which makes a deep copy of nested data?
withDiscount(product, percent) should return a new product with a lower price, leaving the original and its nested tags array unchanged. Also, product.label should be a getter. Fix it.
const product = {
name: "Lamp",
price: 40,
tags: ["home"],
label() { return `${this.name} £${this.price}`; },
};
function withDiscount(item, percent) {
const copy = item;
copy.price = item.price * (1 - percent / 100);
copy.tags.push("sale");
return copy;
}
const sale = withDiscount(product, 25);
console.log(sale.price, product.price, product.tags);- Discounted copy
- Original price unchanged
- Original tags unchanged
- label is a getter
28this & bind()
Read the lessonCreate a version of show with this fixed to item.
const item = { name: "Lamp" };
function show() { return this.name; }
const showItem = show.(item);
console.log(showItem());
bind returns a new function with a fixed this.What is logged?
const a = { n: 1, get() { return this.n; } };
const b = { n: 2 };
b.get = a.get;
console.log(a.get(), b.get());In user.save(), what is this inside save?
What's the difference between call and bind?
stopwatch.start() should increase seconds by 1 every 50ms, and getLabel (a copy of the method) should still return the right label. Fix the this problems.
const stopwatch = {
seconds: 0,
label() { return `${this.seconds}s`; },
start() {
this.id = setInterval(function () {
this.seconds++;
}, 50);
},
};
stopwatch.start();
const getLabel = stopwatch.label;- Seconds increase
- The copied method keeps this
29Destructuring & Spread
Read the lessonPull out name and price, and combine the two arrays.
const product = { name: "Mug", price: 8 };
const { , } = product;
const all = [ a, b ];
....What does this print?
const [a, , c = 9, d = 4] = [1, 2, 3];
console.log(a, c, d);What does this print?
const base = { size: "M", color: "red" };
const order = { ...base, color: "blue", qty: 2 };
console.log(order.color, base.color);color wins in the copy; the original is unchanged.average(…) should accept any number of arguments and return their average. average(2, 4, 6) should be 4. Fix the function.
function average(nums) {
const total = nums.reduce((s, n) => s + n, 0);
return total / nums.length;
}
console.log(average(2, 4, 6));- average(2, 4, 6) is 4
- average(10) is 10
30Sets & Maps
Read the lessonRemove the duplicates from the array.
const ids = [4, 4, 7, 2, 7];
const unique = [...new (ids)];
console.log(unique);
What is logged?
const s = new Set("banana");
console.log(s.size, [...s].join(""));Which method reads a value from a Map?
When is a Map a better choice than an object?
tally(items) should return a Map of each item to how many times it appears. Fix the bugs.
function tally(items) {
const counts = new Map();
for (const item of items) {
counts[item] = counts.get(item) + 1;
}
return counts;
}
const t = tally(["a", "b", "a"]);
console.log(t.get("a"), t.size);- Counts repeats
- Counts single items
- Size is the number of different items
31Dates & Intl
Read the lessonGet the four-digit year from a date.
const d = new Date("2026-05-20");
console.log(d.());
getFullYear() returns the year.What is logged?
const d = new Date(2026, 0, 31);
d.setDate(d.getDate() + 1);
console.log(d.getMonth(), d.getDate());What does getDay() return for a Sunday?
Which formats 9.5 as "$9.50"?
daysBetween(a, b) should return the whole number of days from date a to date b, and christmas(year) should return 25 December of that year. Fix both.
function daysBetween(a, b) {
return (b - a) / 1000;
}
function christmas(year) {
return new Date(year, 12, 25);
}
console.log(daysBetween(new Date(2026, 0, 1), new Date(2026, 0, 11)));
console.log(christmas(2026).toDateString());- Ten days apart
- Christmas is in December
- Same year
32Regular Expressions
Read the lessonMake the pattern match one or more digits.
const hasNumber = /\d/;
console.log(hasNumber.test("Room 101"));
+ means one or more.What is logged?
console.log("cat Cat CAT".match(/cat/gi).length, /^cat$/.test("cats"));isValidCode should accept exactly two capital letters followed by exactly three digits, like "AB123". tidy should replace every run of spaces with a single space. Fix both patterns.
function isValidCode(code) {
return /[A-Z]{2}\d{3}/.test(code);
}
function tidy(text) {
return text.replace(/ +/, " ");
}
console.log(isValidCode("AB123"), isValidCode("xAB1234"), tidy("a b c"));- Accepts a valid code
- Rejects extra characters
- Tidies every gap
33DOM: Selecting Elements
Read the lessonSelect the first .card and all .card elements.
const one = document.(".card");
const all = document.(".card");
querySelector for one, querySelectorAll for all.What does document.querySelector(".missing") return when nothing matches?
You're inside a click handler with the clicked button. How do you get the .product that contains it?
closest walks up the tree.The code should log the number of .task items (3) and the text of the item with id urgent. Fix the selectors.
<ul>
<li class="task">Email Sam</li>
<li class="task" id="urgent">Pay rent</li>
<li class="task">Water plants</li>
</ul>const tasks = document.querySelector("task");
const urgent = document.getElementById("#urgent");
console.log(tasks.length);
console.log(urgent.textContent);- Logs 3
- Logs "Pay rent"
- No errors
34DOM: Changing Elements
Read the lessonChange the text safely and turn on the open class.
const menu = document.querySelector(".menu");
menu. = "Menu (open)";
menu.classList.("open");
textContent sets text; classList.add adds a class.Which is safe to use with text a user typed?
What does el.classList.toggle("dark") do?
The code should add three <li> items to the list and make the title red. Fix the mistakes.
<h3 id="title">Colours</h3>
<ul id="colors"></ul>const colors = ["Red", "Green", "Blue"];
const list = document.querySelector("#colors");
colors.forEach(c => {
const li = document.createElement("<li>");
li.textContent = c;
list.append;
});
document.querySelector("#title").style.colour = "red";- Three list items exist
- Items have the right text
- Title is red
- No errors
35Create & Remove Elements
Read the lessonCreate a paragraph element and add it to the body.
const p = document.("p");
p.textContent = "Hello";
document.body.(p);
createElement, insert with append.What is logged?
const ul = document.createElement("ul");
const a = document.createElement("li");
const b = document.createElement("li");
ul.append(a, b);
ul.prepend(b);
console.log(ul.children.length, ul.firstElementChild === b);Which removes an element from the page?
Why use textContent instead of innerHTML for user data?
addRow(name) should add a new <li> with the name as text to the end of #people, and each row's button should remove only its own row. Fix it.
<ul id="people"></ul>function addRow(name) {
const li = document.createElement("li");
li.innerHTML = name + ' <button>x</button>';
document.querySelector("#people").prepend(li);
li.querySelector("button").addEventListener("click", () => {
document.querySelector("#people").remove();
});
}
addRow("Ana");
addRow("<b>Ben</b>");- Rows are added at the end
- Names are text, not HTML
- A button removes only its row
36Events
Read the lessonRun toggleMenu when the button is clicked.
menuButton.("", toggleMenu);
What does event.preventDefault() do in a submit handler?
Which event fires on every keystroke in a text box?
input fires whenever the value changes.Clicking the button should toggle the dark class on the box and log Dark: true the first time. Fix the bugs.
<div id="box" style="padding:20px;border:2px solid #333">Box</div>
<button id="toggle">Toggle</button>
<style>.dark { background: #15161b; color: white; }</style>const box = document.querySelector("#box");
const toggle = document.querySelector("#toggle");
toggle.addEventListener("onclick", () => {
box.classList.toggle("dark");
console.log("Dark:", box.classList.contains("dark"));
});- Clicking toggles the dark class
- Logs "Dark: true" after a click
37Event Delegation
Read the lessonFind the list item that was clicked, even if the click landed on something inside it.
list.addEventListener("click", (event) => {
const item = event.target.("li");
console.log(item?.textContent);
});
closest climbs up to the nearest matching ancestor.A list ul has a click listener, and you click a <b> inside one of its <li> elements. What are e.target.tagName and e.currentTarget.tagName?
ul.addEventListener("click", (e) => console.log(e.target.tagName, e.currentTarget.tagName));What's the main advantage of event delegation?
Clicking any Remove button, including on rows added later, should remove its own row. Fix it with delegation.
<ul id="rows">
<li>Row 1 <button class="rm">Remove</button></li>
</ul>document.querySelectorAll(".rm").forEach((btn) => {
btn.addEventListener("click", (e) => e.currentTarget.parentElement.parentElement.remove());
});
const extra = document.createElement("li");
extra.innerHTML = 'Row 2 <button class="rm">Remove</button>';
document.querySelector("#rows").append(extra);- The added row's button works
- The list itself survives
38Forms & Validation
Read the lessonStop the form from reloading the page.
form.addEventListener("submit", (event) => {
event.();
console.log("Handled with JavaScript");
});
An input has value="5". What is logged?
console.log(qty.value + 2, qty.valueAsNumber + 2);Which property tells you if a checkbox is ticked?
What does input.setCustomValidity("") do?
On submit, the form should not reload, and #out should show the email and whether "terms" is ticked, like ana@example.com true. Fix it.
<form id="f">
<input name="email" value="ana@example.com">
<input type="checkbox" name="terms" checked>
<button id="go">Go</button>
</form>
<p id="out"></p>const form = document.querySelector("#f");
document.querySelector("#go").addEventListener("click", () => {
const data = new FormData(form);
document.querySelector("#out").textContent = `${data.email} ${form.elements.terms.value}`;
});- Shows the email and ticked state
- Listens for submit
39Timers (setTimeout)
Read the lessonLog "Hi" once after 2 seconds.
(() => console.log("Hi"), );
In what order are the letters logged?
setTimeout(() => console.log("A"), 0);
console.log("B");
setTimeout(() => console.log("C"), 0);
console.log("D");How do you stop an interval?
This should log tick exactly 3 times and then done, but it never stops. Fix it.
let count = 0;
setInterval(() => {
count++;
console.log("tick");
if (count === 3) {
console.log("done");
}
}, 100);- Ticks exactly 3 times then done
40Debounce & Throttle
Read the lessonCancel the previous timer so only the last call runs.
let timer;
function onType(text) {
(timer);
timer = setTimeout(() => console.log(text), 300);
}
onType("a"); onType("ab"); onType("abc");
log is debounced with 100ms. What is logged after a short wait?
const log = debounce((x) => console.log(x), 100);
log(1); log(2); log(3);
setTimeout(() => log(4), 300);Which technique suits a search box that queries a server?
Which technique suits updating a scroll progress bar?
debounce(fn, ms) should return a function that waits until it hasn't been called for ms milliseconds, then calls fn once with the latest arguments. Fix it.
function debounce(fn, ms) {
return (...args) => {
let timer;
clearTimeout(timer);
timer = setTimeout(fn, ms);
};
}
const calls = [];
const d = debounce((v) => calls.push(v), 50);
d("a"); d("b"); d("c");- Runs once with the latest value
- Separate bursts run separately
41localStorage
Read the lessonSave and then read the user's name.
localStorage.("username", "Ola");
const name = localStorage.("username");
What does localStorage.getItem("missing") return?
How do you store an array in localStorage?
The high score should be saved as a number and doubled to log Double: 84. Fix the reading code.
localStorage.setItem("high", JSON.stringify(42));
const high = localStorage.getItem("high");
console.log("Double:", high + high);- Logs "Double: 84"
42JSON
Read the lessonTurn the object into JSON, then back again.
const text = JSON.({ score: 10 });
const data = JSON.(text);
What does this print?
console.log(JSON.stringify({ a: 1, b: undefined, c: null }));The code should parse the saved settings and log Theme: dark. Fix it.
const saved = "{'theme': 'dark', 'size': 16,}";
const settings = JSON.parse(saved);
console.log("Theme:", settings.theme);- Logs "Theme: dark"
- No errors
43Window & Browser APIs
Read the lessonRead the id parameter from the URL's query string.
const params = new (location.search);
console.log(params.get("id"));
URLSearchParams parses ?id=...&....What is logged?
const u = new URL("https://a.com/shop?x=1&x=2#top");
console.log(u.pathname, u.searchParams.get("x"), u.hash);Which changes the URL without reloading the page?
Why use IntersectionObserver instead of a scroll listener?
buildSearchUrl(base, term, page) should return a URL string with q and page parameters, correctly encoded. readPage(url) should return the page as a number, defaulting to 1. Fix both.
function buildSearchUrl(base, term, page) {
return base + "?q=" + term + "&page" + page;
}
function readPage(url) {
return new URL(url).searchParams.get("page");
}
console.log(buildSearchUrl("https://a.com/search", "tea & cake", 2));
console.log(readPage("https://a.com/search?page=4"));- Builds an encoded URL
- Reads the page as a number
- Defaults to page 1
44Responsive JavaScript
Read the lessonCheck whether the screen is at least 1024px wide.
const isLarge = window.("(min-width: 1024px)").matches;
console.log(isLarge);
matchMedia runs a CSS media query from JavaScript.The window starts at 500px wide and is dragged slowly to 900px. How many times does this log?
matchMedia("(min-width: 768px)").addEventListener("change", () => console.log("changed"));Which query detects that the user prefers less animation?
Which tool reacts when a sidebar opens and a chart's container gets narrower, without the window changing size?
watchLayout(callback) should call callback(true) or callback(false) immediately for whether the viewport is at least 700px wide, then again each time that changes. It should return the MediaQueryList. Fix it.
function watchLayout(callback) {
const mq = window.matchMedia("min-width: 700px");
window.addEventListener("resize", () => callback(mq.matches));
return mq;
}
watchLayout((wide) => console.log("wide?", wide));- Uses a valid media query
- Calls back immediately
- Listens for change on the query
45Canvas & Animation
Read the lessonGet the 2D drawing context.
const ctx = canvas.("2d");
ctx.fillRect(10, 10, 50, 50);
getContext("2d") returns the drawing API.On a canvas, where is the point (0, 100)?
ctx.fillRect(0, 100, 10, 10);Which function should drive a smooth animation loop?
Why multiply speed by elapsed time each frame?
drawDots(ctx, points) should clear the whole canvas, then draw a separate filled circle of radius 5 at each point. Fix the three bugs.
<canvas id="c" width="200" height="100"></canvas>function drawDots(ctx, points) {
ctx.clearRect(0, 0, 10, 10);
for (const p of points) {
ctx.arc(p.x, p.y, 5, 0, Math.PI);
ctx.fill();
}
}
const ctx = document.querySelector("#c").getContext("2d");
drawDots(ctx, [{ x: 20, y: 20 }, { x: 100, y: 50 }]);- Draws full circles
- Clears the whole canvas
- Circles are separate shapes
46Errors (try/catch)
Read the lessonCatch the error and print its message.
{
JSON.parse("{bad json}");
} (error) {
console.log(error.message);
}
try { } catch (error) { } handles run-time errors.What does ReferenceError: total is not defined usually mean?
What does this print?
try {
console.log("A");
throw new Error("B");
console.log("C");
} catch (e) {
console.log(e.message);
} finally {
console.log("D");
}divide(10, 0) should throw an Error with the message Cannot divide by zero, and the code should catch it and log that message. Fix it.
function divide(a, b) {
if (b === 0) {
return "Cannot divide by zero";
}
return a / b;
}
try {
divide(10, 0);
} catch (error) {
console.log(error.message);
}- divide(10, 2) is 5
- divide(1, 0) throws an Error
- Logs the message
47Debugging
Read the lessonShow an array of objects as a table in the console.
const people = [{ name: "Ana", age: 31 }, { name: "Ben", age: 27 }];
console.(people);
console.table displays rows and columns.What does this error message tell you?
// TypeError: Cannot read properties of undefined (reading 'length')
console.log(order.items.length);Which statement pauses code when DevTools is open?
Which stepping control runs the current line without entering the functions it calls?
average(scores) should return the mean of the numbers, but some scores arrive as strings and one is missing. Convert strings to numbers, skip missing (null or undefined) values, and return 0 for an empty list.
function average(scores) {
let total = 0;
for (const s of scores) total += s;
return total / scores.length;
}
console.log(average([10, "20", null, 30]));- Handles strings and missing values
- Plain numbers still work
- Empty list gives 0
48Promises
Read the lessonHandle success and failure.
loadScores()
.(scores => console.log(scores))
.(error => console.log("Failed:", error.message));
then for success, catch for errors.In what order are the numbers logged?
console.log(1);
Promise.resolve().then(() => console.log(2));
console.log(3);.then callbacks always run after the current code finishes.What does Promise.all do if one promise rejects?
The chain should log Total: 30. A value is being lost. Fix it.
Promise.resolve(10)
.then(n => {
n * 3;
})
.then(total => console.log("Total:", total));- Logs "Total: 30"
49async / await
Read the lessonMake the function async and wait for the data.
function start() {
const data = loadData();
console.log(data);
}
await only works inside async functions.What does an async function always return?
return 5 becomes a Promise that resolves to 5.In what order are the letters logged?
async function run() {
console.log("B");
await null;
console.log("D");
}
console.log("A");
run();
console.log("C");getTotal should wait for both prices and log Total: 30. Fix it.
const price = (n) => new Promise(r => setTimeout(() => r(n), 100));
function getTotal() {
const a = price(10);
const b = price(20);
console.log("Total:", a + b);
}
getTotal();- Logs "Total: 30"
- getTotal is async
50Event Loop
Read the lessonSchedule a microtask.
(() => console.log("microtask"));
console.log("sync");
queueMicrotask adds a callback to the microtask queue.What is logged?
setTimeout(() => console.log("t"), 0);
Promise.resolve().then(() => console.log("p"));
console.log("s");How many things can JavaScript's main thread do at once?
order() should resolve with the array ["sync", "micro", "task"], recording in the order things actually run. The pushes are in the wrong kinds of callbacks. Fix it without changing the final array.
function order() {
const log = [];
return new Promise((resolve) => {
Promise.resolve().then(() => log.push("task"));
setTimeout(() => {
log.push("micro");
resolve(log);
}, 0);
log.push("sync");
});
}
order().then((log) => console.log(log));- Records the real order
51Fetch API
Read the lessonRead the JSON body.
const res = await (url);
const data = await res.();
.json() parses its body.What does fetch do when the server replies with 404?
Which option sends JSON data to a server?
getTitle(1) should return the title of todo 1 ("delectus aut autem") and log it. Fix the function.
async function getTitle(id) {
const res = fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
const todo = res.json();
return todo.title;
}
getTitle(1).then(t => console.log(t));- getTitle(1) returns the title
52Classes
Read the lessonComplete the class.
class Book {
(title) {
.title = title;
}
}
const b = Book("Dune");
constructor sets up the instance via this; new creates one.What does super(name) do inside a child class constructor?
this in a child constructor.What does this print?
class Counter {
#n = 0;
inc() { this.#n++; return this.#n; }
}
const c = new Counter();
c.inc(); c.inc();
console.log(c.inc(), c.n);#n is private, so c.n is a different, non-existent property.new Square(4).area should be 16, and Square should extend Shape. Fix it.
class Shape {
constructor(name) {
this.name = name;
}
}
class Square extends Shape {
constructor(side) {
this.side = side;
super("square");
}
get area() {
return side * side;
}
}
console.log(new Square(4).area);- Area of a 4-sided square is 16
- Square has the name "square"
- Square is a Shape
53Prototypes
Read the lessonCreate cat with animal as its prototype.
const animal = { eats: true };
const cat = Object.(animal);
console.log(cat.eats);
Object.create(proto) sets the new object's prototype.What is logged?
const base = { colour: "red" };
const item = Object.create(base);
item.colour = "blue";
console.log(item.colour, base.colour, Object.keys(item).length);Where do array methods like map come from?
What does new do first when calling a constructor function?
Every Robot should share one greet method from the prototype, and greet should use the robot's own name. Fix it.
function Robot(name) {
this.name = name;
}
Robot.greet = () => `Beep, I am ${this.name}`;
const r1 = new Robot("R1");
const r2 = new Robot("R2");
console.log(r1.greet?.());- Robots can greet
- The method is shared
- The method lives on the prototype
54Iterators & Generators
Read the lessonHand out each value from the generator.
function* colours() {
"red";
"green";
}
console.log([...colours()]);
yield hands out a value and pauses.What is logged?
function* g() { yield 1; yield 2; return 3; }
const it = g();
console.log(it.next().value, [...it].length);What does an iterator's next() return?
Which key makes an object work with for...of?
range(start, end) should be a generator that yields every whole number from start to end inclusive, so [...range(2, 5)] is [2, 3, 4, 5]. Fix it.
function range(start, end) {
for (let i = start; i < end; i++) {
return i;
}
}
console.log([...range(2, 5)]);- Yields an inclusive range
- Single value range
- Is a generator
55Modules (import/export)
Read the lessonExport a function and import it in another file.
// greet.js
function hello(name) { return `Hello ${name}`; }
// main.js
{ hello } "./greet.js";
export shares it; import { name } from "path" uses it.How many default exports can a module have?
Which script tag lets you use import?
The module should import double (a named export) and log Double: 42. Fix the import.
<script type="module">
import double from "data:text/javascript,export const double = n => n * 2;";
console.log("Double:", double(21));
</script>- Logs "Double: 42"
- Uses a named import
56Web Workers
Read the lessonSend data to the worker.
const worker = new Worker("worker.js");
worker.({ task: "sort", items: [3, 1, 2] });
postMessage sends a copy of the data.What happens to the original array after this runs?
const list = [1, 2, 3];
worker.postMessage(list);
list.push(4);
// inside the worker: console.log(e.data.length)What is the main reason to use a Web Worker?
fetchWithTimeout(promiseFactory, ms) should call promiseFactory(signal) with an AbortController signal, and abort it if it takes longer than ms. It should resolve with the result when fast enough. Fix it.
function fetchWithTimeout(promiseFactory, ms) {
const controller = new AbortController();
setTimeout(() => controller.abort, ms);
return promiseFactory();
}
const slow = (signal) => new Promise((resolve, reject) => {
const t = setTimeout(() => resolve("data"), 200);
signal?.addEventListener("abort", () => { clearTimeout(t); reject(new Error("aborted")); });
});
fetchWithTimeout(slow, 50).then(console.log, (e) => console.log(e.message));- Slow tasks are aborted
- Fast tasks resolve
No exercises match those filters.