JavaScript Workshops: Practical Code Scenarios
This guide provides a collection of hands-on code scenarios and “mini-projects” that demonstrate core JavaScript concepts in action. Each example is heavily commented to explain the why and how behind the code.
Table of Contents
- Logic & Control Flow: Season Determiner
- Array Manipulation: Shopping Cart Logic
- DOM Interaction: Interactive Color Picker
- Data Persistence: Local Storage Form
- Event Delegation: Dynamic List Management
1. Logic & Control Flow: Season Determiner
Concepts: Functions, if/else if/else, Logical Operators (&&), Return values.
This function determines the season based on a given month number (1-12).
/**
* Determines the season based on the month number.
*
* @param {number} month - The month number (1 for January, 12 for December).
* @returns {string} - The name of the season ("Spring", "Summer", "Fall", "Winter") or "Invalid Month".
*/
function getSeason(month) {
// Check if the month is valid (must be between 1 and 12)
if (month < 1 || month > 12) {
return "Invalid Month";
}
// Spring: March (3), April (4), May (5)
// We use the AND operator (&&) to check ranges.
if (month >= 3 && month <= 5) {
return "Spring";
}
// Summer: June (6), July (7), August (8)
else if (month >= 6 && month <= 8) {
return "Summer";
}
// Fall: September (9), October (10), November (11)
else if (month >= 9 && month <= 11) {
return "Fall";
}
// Winter: December (12), January (1), February (2)
// This is the "catch-all" else because we already validated 1-12 at the start.
else {
return "Winter";
}
}
// === Testing the Function ===
console.log(getSeason(4)); // Output: Spring
console.log(getSeason(8)); // Output: Summer
console.log(getSeason(1)); // Output: Winter
console.log(getSeason(13)); // Output: Invalid Month
2. Array Manipulation: Shopping Cart Logic
Concepts: Objects, Arrays, this keyword, Array Methods (push, find, findIndex, splice, reduce).
A simplified shopping cart object that manages items, prices, and quantities.
const shoppingCart = {
// The cart's storage: an array of item objects
items: [],
/**
* Adds an item to the cart. If the item exists, increases quantity.
* @param {string} name - Name of the product
* @param {number} price - Price per unit
* @param {number} quantity - Quantity to add
*/
addItem: function(name, price, quantity) {
// 1. Check if item already exists in the array
// .find() returns the *first* element that matches the condition, or undefined.
const existingItem = this.items.find(item => item.name === name);
if (existingItem) {
// Logic: If item exists, just update the quantity property.
console.log(`Updating quantity for ${name}...`);
existingItem.quantity += quantity;
} else {
// Logic: If it's new, create a new object and push it to the array.
console.log(`Adding new item: ${name}...`);
const newItem = {
name: name,
price: price,
quantity: quantity
};
this.items.push(newItem);
}
},
/**
* Removes an item completely from the cart by name.
* @param {string} name - Name of the product to remove
*/
removeItem: function(name) {
// 1. Find the index of the item
// .findIndex() returns the index (0, 1, 2...) or -1 if not found.
const index = this.items.findIndex(item => item.name === name);
if (index !== -1) {
// 2. Use .splice() to modify the array in place.
// splice(startIndex, deleteCount)
this.items.splice(index, 1);
console.log(`${name} removed from cart.`);
} else {
console.log(`${name} not found in cart.`);
}
},
/**
* Calculates the total cost of all items.
* @returns {number} - Total cost
*/
calculateTotal: function() {
// .reduce() accumulates a value by iterating over the array.
// acc: accumulator (running total)
// item: current item in the loop
// 0: initial value of the accumulator
return this.items.reduce((acc, item) => {
return acc + (item.price * item.quantity);
}, 0);
}
};
// === Testing the Cart ===
shoppingCart.addItem("Apple", 1.50, 2); // Add 2 Apples
shoppingCart.addItem("Banana", 0.80, 5); // Add 5 Bananas
shoppingCart.addItem("Apple", 1.50, 3); // Add 3 more Apples (Total 5)
console.log("Current Cart:", shoppingCart.items);
// Output: [{name: "Apple", price: 1.5, quantity: 5}, {name: "Banana", ...}]
console.log("Total Cost: $" + shoppingCart.calculateTotal());
// (5 * 1.50) + (5 * 0.80) = 7.50 + 4.00 = 11.50
shoppingCart.removeItem("Banana");
console.log("Cart after removing Banana:", shoppingCart.items);
3. DOM Interaction: Interactive Color Picker
Concepts: DOM Selection (querySelectorAll, getElementById), Event Listeners (click), Style Manipulation, dataset attributes.
HTML Structure (assumed):
<div class="swatch" data-color="red" style="background-color: red;"></div>
<div class="swatch" data-color="blue" style="background-color: blue;"></div>
<div id="canvas" style="width: 200px; height: 200px; border: 1px solid black;"></div>
JavaScript Implementation:
// 1. Select all elements with the class 'swatch'
// querySelectorAll returns a NodeList (similar to an array)
const swatches = document.querySelectorAll(".swatch");
// 2. Select the target element we want to change
const canvas = document.getElementById("canvas");
/**
* Helper function to handle the color change logic.
* @param {string} color - The color string (e.g., "red", "#ff0000")
*/
const updateCanvasColor = (color) => {
console.log(`Changing background to: ${color}`);
canvas.style.backgroundColor = color;
// Optional: Add a text indicator
canvas.textContent = `Current Color: ${color}`;
canvas.style.color = "white"; // Ensure text is visible
canvas.style.display = "flex";
canvas.style.justifyContent = "center";
canvas.style.alignItems = "center";
};
// 3. Loop through each swatch to attach an event listener
swatches.forEach(swatch => {
swatch.addEventListener("click", (event) => {
// 'event.target' refers to the specific element that was clicked
// Access custom data attributes using .dataset
// HTML: data-color="red" -> JS: dataset.color
const selectedColor = event.target.dataset.color;
// Call our helper function
updateCanvasColor(selectedColor);
});
});
4. Data Persistence: Local Storage Form
Concepts: localStorage API (setItem, getItem), JSON serialization (JSON.stringify, JSON.parse), Form Events (submit), Preventing default browser behavior.
HTML Structure (assumed):
<form id="preferences-form">
<input type="text" id="username" placeholder="Enter username" />
<select id="theme">
<option value="light">Light Mode</option>
<option value="dark">Dark Mode</option>
</select>
<button type="submit">Save Preferences</button>
</form>
JavaScript Implementation:
const form = document.getElementById("preferences-form");
const usernameInput = document.getElementById("username");
const themeInput = document.getElementById("theme");
// 1. Function to load saved data when the page opens
const loadPreferences = () => {
// Retrieve string data from Local Storage
const savedData = localStorage.getItem("userPreferences");
if (savedData) {
// Convert string back into a JavaScript Object
const preferences = JSON.parse(savedData);
// Pre-fill form inputs
usernameInput.value = preferences.username;
themeInput.value = preferences.theme;
console.log("Preferences loaded:", preferences);
// Optional: Apply theme immediately
document.body.className = preferences.theme;
}
};
// 2. Event Listener for Form Submission
form.addEventListener("submit", (event) => {
// CRITICAL: Prevent the browser from refreshing the page
event.preventDefault();
// Create an object with the current form values
const preferences = {
username: usernameInput.value,
theme: themeInput.value
};
// Save to Local Storage
// Local Storage ONLY stores strings, so we must use JSON.stringify()
localStorage.setItem("userPreferences", JSON.stringify(preferences));
console.log("Preferences saved!", preferences);
alert("Settings saved successfully.");
});
// 3. Run load function immediately when script runs (page load)
loadPreferences();
5. Event Delegation: Dynamic List Management
Concepts: Event Delegation (listening on parent), Event Bubbling, Dynamic Element Creation.
Scenario: A To-Do list where items can be added dynamically. We want to click a “Delete” button on any item, even ones created after the page loaded.
HTML Structure (assumed):
<ul id="todo-list">
<li>Buy Milk <button class="delete-btn">X</button></li>
</ul>
<button id="add-btn">Add Task</button>
JavaScript Implementation:
const todoList = document.getElementById("todo-list");
const addBtn = document.getElementById("add-btn");
// 1. Add Event Listener to the PARENT (<ul>), not individual items.
// This is "Event Delegation". The event "bubbles up" from the button to the ul.
todoList.addEventListener("click", (event) => {
// Check WHAT was clicked inside the list
const clickedElement = event.target;
// We only care if the clicked element has the class 'delete-btn'
if (clickedElement.classList.contains("delete-btn")) {
// Find the parent <li> of the clicked button
const listItem = clickedElement.parentElement;
// Remove the <li> from the DOM
listItem.remove();
console.log("Task removed!");
}
});
// 2. Logic to add new items (to demonstrate delegation works on new items)
addBtn.addEventListener("click", () => {
const newItem = document.createElement("li");
newItem.innerHTML = `New Task ${Date.now()} <button class="delete-btn">X</button>`;
// Append to the list
todoList.appendChild(newItem);
console.log("New task added.");
// Notice we did NOT add a click listener to the new button.
// The parent <ul> listener handles it automatically!
});