๐Ÿ’ป
How to use: Open browser DevTools (F12) โ†’ Console tab. Type each solution there. For DOM exercises, create an HTML file, open it in your browser, and link a script.js file.

Exercises 1โ€“5: JavaScript Core

Exercise 1 โ€” Variables & Types

Create variables for: your name (string), age (number), whether you're a student (boolean), and a list of your top 3 programming languages (array). Log each one with its type using typeof.

Show Answer
const name     = "Uzair";
const age      = 25;
const isStudent = true;
const languages = ["Python", "JavaScript", "SQL"];

console.log(name,      typeof name);
console.log(age,       typeof age);
console.log(isStudent, typeof isStudent);
console.log(languages, typeof languages);

Exercise 2 โ€” Functions

Write three versions of the same function (convert Celsius to Fahrenheit): as a function declaration, a function expression, and an arrow function. Test all three with 100ยฐC.

Show Answer
// Declaration
function toF1(c) { return c * 9/5 + 32; }

// Expression
const toF2 = function(c) { return c * 9/5 + 32; };

// Arrow
const toF3 = c => c * 9/5 + 32;

console.log(toF1(100)); // 212
console.log(toF2(100)); // 212
console.log(toF3(100)); // 212

Exercise 3 โ€” Array Methods

Given const nums = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3], use array methods to: (a) get unique values only, (b) square all values, (c) filter values greater than 4, (d) get their sum. One line each.

Show Answer
const nums = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3];

// a) Unique
const unique = [...new Set(nums)];

// b) Squared
const squared = nums.map(n => n ** 2);

// c) Greater than 4
const big = nums.filter(n => n > 4);

// d) Sum
const sum = nums.reduce((acc, n) => acc + n, 0);

console.log(unique);  // [3,1,4,5,9,2,6]
console.log(squared); // [9,1,16,1,25,81,4,36,25,9]
console.log(big);     // [5,9,6,5]
console.log(sum);     // 39

Exercise 4 โ€” Object Destructuring

Given a user object, destructure it to extract: name, email, and the city from the nested address. Then use spread to create an updated copy with a different age.

Show Answer
const user = {
    name: "Alice",
    age: 25,
    email: "alice@example.com",
    address: { city: "London", postcode: "EC1A 1BB" }
};

const { name, email, address: { city } } = user;
console.log(name, email, city); // Alice alice@example.com London

const updated = { ...user, age: 26 };
console.log(updated.age); // 26
console.log(user.age);    // 25 (original unchanged)

Exercise 5 โ€” Error Handling

Write a function divide(a, b) that throws an error if b is 0. Wrap calls in try/catch and log a helpful message for each case. Test with valid and invalid inputs.

Show Answer
function divide(a, b) {
    if (b === 0) throw new Error("Cannot divide by zero");
    return a / b;
}

[{a:10,b:2}, {a:7,b:0}, {a:9,b:3}].forEach(({a, b}) => {
    try {
        console.log(`${a} / ${b} = ${divide(a, b)}`);
    } catch (err) {
        console.error(`Error for ${a}/${b}: ${err.message}`);
    }
});

Exercises 6โ€“10: DOM & Events

Setup for DOM exercises: Create index.html with a linked script.js. Add elements as needed per exercise.

Exercise 6 โ€” DOM Selection & Modification

Create a page with an <h1> and 3 <p> tags. Using JavaScript (not CSS): change the h1 colour to red, double the font-size of all paragraphs, add a CSS class "highlight" to the second paragraph.

Show Answer
// HTML setup:
// <h1 id="title">Hello</h1>
// <p>Para 1</p> <p>Para 2</p> <p>Para 3</p>

const title = document.querySelector("h1");
title.style.color = "red";

const paras = document.querySelectorAll("p");
paras.forEach(p => {
    const current = parseFloat(getComputedStyle(p).fontSize);
    p.style.fontSize = (current * 2) + "px";
});

paras[1].classList.add("highlight");

Exercise 7 โ€” Live Input Counter

Create a textarea with a character counter below it (e.g., "42 / 280 characters"). The counter updates in real-time as the user types. Turn the text red when over 250 characters.

Show Answer
// HTML:
// <textarea id="msg" rows="4" maxlength="280"></textarea>
// <div id="counter">0 / 280</div>

const textarea = document.getElementById("msg");
const counter  = document.getElementById("counter");

textarea.addEventListener("input", () => {
    const len = textarea.value.length;
    counter.textContent = `${len} / 280`;
    counter.style.color = len > 250 ? "red" : "";
});

Exercise 8 โ€” Show/Hide Toggle

Create a "Spoiler" button. When clicked it shows hidden content (a paragraph of text). When clicked again it hides it. Animate the transition smoothly.

Show Answer
// HTML:
// <button id="toggle">Show Spoiler</button>
// <p id="spoiler" style="display:none;transition:opacity 0.3s">
//   The butler did it!
// </p>

const btn     = document.getElementById("toggle");
const spoiler = document.getElementById("spoiler");

btn.addEventListener("click", () => {
    const hidden = spoiler.style.display === "none";
    spoiler.style.display = hidden ? "block" : "none";
    spoiler.style.opacity = hidden ? "1" : "0";
    btn.textContent = hidden ? "Hide Spoiler" : "Show Spoiler";
});

Exercise 9 โ€” Form Validation

Create a sign-up form (username, email, password, confirm password). Validate on submit: username min 3 chars, valid email format, password min 8 chars with a number, passwords match. Show inline error messages.

Show Answer
document.getElementById("signupForm").addEventListener("submit", e => {
    e.preventDefault();
    const username = document.getElementById("username").value;
    const email    = document.getElementById("email").value;
    const pass     = document.getElementById("password").value;
    const confirm  = document.getElementById("confirm").value;
    const errors   = [];

    if (username.length < 3)
        errors.push("Username must be at least 3 characters");
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
        errors.push("Enter a valid email address");
    if (pass.length < 8 || !/\d/.test(pass))
        errors.push("Password must be 8+ chars and contain a number");
    if (pass !== confirm)
        errors.push("Passwords do not match");

    const errDiv = document.getElementById("errors");
    if (errors.length) {
        errDiv.innerHTML = errors.map(e => `<p style="color:red">${e}</p>`).join("");
    } else {
        errDiv.innerHTML = "<p style='color:green'>Success!</p>";
    }
});

Exercise 10 โ€” Keyboard Shortcut Handler

Listen for keyboard shortcuts: Ctrl+S โ†’ log "Saved!", Ctrl+Z โ†’ log "Undo!", Escape โ†’ close any open modal. Prevent browser defaults where applicable.

Show Answer
document.addEventListener("keydown", e => {
    if (e.ctrlKey && e.key === "s") {
        e.preventDefault();
        console.log("Saved!");
    } else if (e.ctrlKey && e.key === "z") {
        e.preventDefault();
        console.log("Undo!");
    } else if (e.key === "Escape") {
        const modal = document.querySelector(".modal");
        if (modal) modal.style.display = "none";
        console.log("Modal closed (Escape)");
    }
});

Exercises 11โ€“15: Arrays, Logic & Async

Exercise 11 โ€” Sort & Search

Given an array of 10 people objects (name, age, city), write functions to: sort by age ascending, filter by city, find a person by name, and get the average age.

Show Answer
const people = [
    {name:"Alice", age:25, city:"London"},
    {name:"Bob",   age:30, city:"Paris"},
    {name:"Carol", age:22, city:"London"},
    {name:"Dave",  age:35, city:"Berlin"},
    {name:"Eve",   age:28, city:"London"},
];

const byAge      = [...people].sort((a,b) => a.age - b.age);
const inLondon   = people.filter(p => p.city === "London");
const findAlice  = people.find(p => p.name === "Alice");
const avgAge     = people.reduce((s,p) => s + p.age, 0) / people.length;

console.log("Sorted:", byAge.map(p => p.name));
console.log("London:", inLondon.map(p => p.name));
console.log("Found:", findAlice);
console.log("Avg age:", avgAge);

Exercise 12 โ€” Debounce

Implement a debounce function that wraps another function and only calls it after the user stops typing for a specified delay. Test it on a search input that logs the query.

Show Answer
function debounce(fn, delay) {
    let timer;
    return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
    };
}

const search = debounce((query) => {
    console.log("Searching for:", query);
}, 500);

// Attach to input:
document.getElementById("searchInput").addEventListener("input", e => {
    search(e.target.value);
});

Exercise 13 โ€” Local Storage

Build a preferences saver: let the user pick a theme (dark/light), font size (small/medium/large), and language. Save to localStorage. On page load, restore saved preferences automatically.

Show Answer
const defaults = { theme: "dark", fontSize: "medium", language: "en" };

function loadPrefs() {
    const saved = JSON.parse(localStorage.getItem("prefs")) || defaults;
    applyPrefs(saved);
    document.getElementById("theme").value    = saved.theme;
    document.getElementById("fontSize").value = saved.fontSize;
    document.getElementById("language").value = saved.language;
}

function applyPrefs(prefs) {
    document.body.className = prefs.theme;
    document.body.style.fontSize = { small:"14px", medium:"16px", large:"20px" }[prefs.fontSize];
    console.log(`Language: ${prefs.language}`);
}

document.getElementById("saveBtn").addEventListener("click", () => {
    const prefs = {
        theme:    document.getElementById("theme").value,
        fontSize: document.getElementById("fontSize").value,
        language: document.getElementById("language").value,
    };
    localStorage.setItem("prefs", JSON.stringify(prefs));
    applyPrefs(prefs);
    console.log("Saved:", prefs);
});

loadPrefs();

Exercise 14 โ€” Fetch & Display

Fetch users from https://jsonplaceholder.typicode.com/users and display them as cards on the page. Each card shows: name, email, company name, and website. Add a loading spinner while fetching.

Show Answer
// HTML: <div id="loading">Loading...</div><div id="cards"></div>

async function loadUsers() {
    const loading = document.getElementById("loading");
    const cards   = document.getElementById("cards");

    loading.style.display = "block";
    try {
        const res   = await fetch("https://jsonplaceholder.typicode.com/users");
        const users = await res.json();

        cards.innerHTML = users.map(u => `
            <div style="border:1px solid #333;padding:16px;border-radius:8px;margin:12px 0">
                <h3>${u.name}</h3>
                <p>๐Ÿ“ง ${u.email}</p>
                <p>๐Ÿข ${u.company.name}</p>
                <p>๐ŸŒ ${u.website}</p>
            </div>
        `).join("");
    } catch (err) {
        cards.innerHTML = `<p style="color:red">Error: ${err.message}</p>`;
    } finally {
        loading.style.display = "none";
    }
}
loadUsers();

Exercise 15 โ€” Promise Chain

Write a function that simulates a multi-step process using Promises: step 1 validates input (1s delay), step 2 saves to "database" (2s delay), step 3 sends confirmation email (1s delay). Log progress at each step.

Show Answer
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

async function processSignup(username) {
    console.log("Starting signup for:", username);

    console.log("Step 1: Validating...");
    await delay(1000);
    if (username.length < 3) throw new Error("Username too short");
    console.log("โœ“ Validation passed");

    console.log("Step 2: Saving to database...");
    await delay(2000);
    const userId = Math.floor(Math.random() * 10000);
    console.log(`โœ“ Saved with ID: ${userId}`);

    console.log("Step 3: Sending confirmation email...");
    await delay(1000);
    console.log("โœ“ Email sent!");

    return { userId, username };
}

processSignup("Uzair")
    .then(result => console.log("Done!", result))
    .catch(err   => console.error("Failed:", err.message));

Exercises 16โ€“20: Mini Projects

Exercise 16 โ€” Countdown Timer

Build a countdown timer: user enters minutes, clicks Start. Timer counts down and shows MM:SS. Buttons: Start, Pause, Reset. When it hits 0, play an alert sound (or show an alert).

Show Answer
// HTML: <input id="mins" type="number" value="5"/>
// <div id="display">05:00</div>
// <button id="start">Start</button>
// <button id="pause">Pause</button>
// <button id="reset">Reset</button>

let total, timer, running = false;

function format(secs) {
    const m = String(Math.floor(secs/60)).padStart(2,"0");
    const s = String(secs%60).padStart(2,"0");
    return `${m}:${s}`;
}

document.getElementById("start").addEventListener("click", () => {
    if (!running) {
        if (!total) total = parseInt(document.getElementById("mins").value) * 60;
        running = true;
        timer = setInterval(() => {
            total--;
            document.getElementById("display").textContent = format(total);
            if (total <= 0) {
                clearInterval(timer);
                running = false;
                alert("Time's up!");
            }
        }, 1000);
    }
});
document.getElementById("pause").addEventListener("click", () => {
    clearInterval(timer); running = false;
});
document.getElementById("reset").addEventListener("click", () => {
    clearInterval(timer); running = false; total = 0;
    document.getElementById("display").textContent = "00:00";
});

Exercise 17 โ€” Quiz App

Build a 5-question quiz: show one question at a time with 4 options, track score, show result at end. Include a progress bar and allow restart.

Show Answer
const questions = [
    {q:"What does HTML stand for?", options:["HyperText Markup Language","High Transfer Markup Language","HyperText Machine Language","None"],correct:0},
    {q:"Which of these is NOT a JavaScript data type?", options:["String","Boolean","Float","Undefined"],correct:2},
    {q:"What does CSS stand for?", options:["Cascading Style Sheets","Computer Style System","Creative Syntax Styles","None"],correct:0},
    {q:"Which method adds an element to the end of an array?", options:["push()","pop()","shift()","append()"],correct:0},
    {q:"What does SQL stand for?", options:["Structured Query Language","Simple Query Logic","Standard Question Language","None"],correct:0},
];

let current = 0, score = 0;

function showQuestion() {
    const q = questions[current];
    document.getElementById("question").textContent = q.q;
    document.getElementById("progress").textContent = `Q${current+1}/${questions.length}`;
    document.getElementById("options").innerHTML = q.options.map((opt,i) =>
        `<button onclick="answer(${i})">${opt}</button>`
    ).join("");
}

function answer(i) {
    if (i === questions[current].correct) score++;
    current++;
    if (current < questions.length) showQuestion();
    else document.getElementById("app").innerHTML = `
        <h2>Score: ${score}/${questions.length}</h2>
        <button onclick="restart()">Restart</button>
    `;
}
function restart() { current = 0; score = 0; showQuestion(); }
showQuestion();

Exercise 18 โ€” Drag & Drop Kanban

Build a 3-column Kanban board (To Do, In Progress, Done) where tasks can be dragged between columns. Allow adding new tasks. Save state to localStorage.

Challenge: This is a sizeable project. Use the HTML5 Drag and Drop API (draggable, ondragstart, ondrop). Stretch goal: add task priority colours.

Exercise 19 โ€” Weather App

Build a weather app using the Open-Meteo API (free, no API key): fetch current weather for a city using its coordinates. Show temperature, wind speed, and weather code. Let the user enter a city name.

Show Answer
// Open-Meteo is free and needs lat/lon. Use Nominatim for geocoding.
async function getWeather(city) {
    // Step 1: geocode city name to lat/lon
    const geoRes = await fetch(`https://nominatim.openstreetmap.org/search?q=${city}&format=json&limit=1`);
    const geoData = await geoRes.json();
    if (!geoData.length) throw new Error("City not found");
    const { lat, lon, display_name } = geoData[0];

    // Step 2: get weather
    const weatherRes = await fetch(
        `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t_weather=true`
    );
    const weather = await weatherRes.json();
    const cw = weather.current_weather;

    document.getElementById("result").innerHTML = `
        <h3>${display_name.split(",")[0]}</h3>
        <p>๐ŸŒก๏ธ Temperature: ${cw.temperature}ยฐC</p>
        <p>๐Ÿ’จ Wind speed: ${cw.windspeed} km/h</p>
        <p>๐Ÿ• Updated: ${cw.time}</p>
    `;
}

document.getElementById("searchBtn").addEventListener("click", async () => {
    const city = document.getElementById("cityInput").value;
    try { await getWeather(city); }
    catch(e) { document.getElementById("result").innerHTML = `Error: ${e.message}`; }
});

Exercise 20 โ€” Full CRUD App

Build a full Notes CRUD app: Create notes (title + body), Read/list all notes, Update (click to edit), Delete. Store in localStorage. Add search/filter. Style it properly.

๐Ÿ†
Boss Level! This is your JavaScript capstone. A well-built notes app demonstrates all core JS skills: DOM manipulation, events, data persistence, CRUD operations, and clean code organisation. Make it look good enough to put in your portfolio.
โ† JS Workbook All Labs
// guided terminal

Try It Live

Type the commands from the steps above. The terminal simulates the expected output for this lab.

KaliRange ~ Terminal type help for commands