Project: Image Changer with DOM and Events
This project uses DOM manipulation and events to change the displayed image when the user clicks a button.
Change the Image
let imageIndex = 1;
function changeImage() {
imageIndex = imageIndex === 1 ? 2 : 1;
const img = document.getElementById("image");
img.src = `images/img${imageIndex}.png`;
}
Project: Character Counter with Validation
This project uses DOM manipulation and events to count the characters entered in a text field and validate if the limit has been exceeded.
const textarea = document.getElementById("comment");
const counter = document.getElementById("counter");
const error = document.getElementById("error");
const limit = 100;
textarea.addEventListener("input", () => {
const length = textarea.value.length;
counter.textContent = `${length} / ${limit} characters`;
if (length > limit) {
error.textContent = "You have exceeded the character limit!";
counter.style.color = "red";
} else {
error.textContent = "";
counter.style.color = "black";
}
});
function submitComment() {
const length = textarea.value.length;
if (length > limit) {
alert("The comment is too long!");
} else if (length === 0) {
alert("The comment cannot be empty!");
} else {
alert("Comment submitted!");
textarea.value = "";
counter.textContent = `0 / ${limit} characters`;
counter.style.color = "black";
}
}
Write a Comment
0 / 100 characters
Project: To-Do List App
This project creates a simple to-do list application that allows the user to add, check off, and delete tasks.
Add a Task
function addTask() {
const input = document.getElementById("taskInput");
const value = input.value.trim();
if (value === "") {
alert("Please enter a task!");
return;
}
const li = document.createElement("li");
li.textContent = value;
li.style.margin = "8px 0";
li.style.cursor = "pointer";
li.onclick = function () {
li.style.textDecoration = li.style.textDecoration === "line-through" ? "none" : "line-through";
li.style.color = li.style.textDecoration === "line-through" ? "gray" : "black";
};
const deleteBtn = document.createElement("button");
deleteBtn.textContent = "Delete";
deleteBtn.style.marginLeft = "10px";
deleteBtn.style.padding = "4px 8px";
deleteBtn.onclick = function () {
li.remove();
};
li.appendChild(deleteBtn);
document.getElementById("taskList").appendChild(li);
input.value = "";
}
Project: Secret Message Encoder
This project uses JavaScript functions to encode and decode secret messages using simple methods like Base64.
Encode a Message
Decode a Message
function encodeMessage() {
const input = document.getElementById("messageInput").value;
if (input.trim() === "") {
alert("Write a message before encoding!");
return;
}
const encoded = btoa(input);
document.getElementById("encodedMessage").textContent = `Encoded: ${encoded}`;
}
function decodeMessage() {
const input = document.getElementById("encodedMessageInput").value;
try {
const decoded = atob(input);
document.getElementById("decodedMessage").textContent = `Decoded: ${decoded}`;
} catch (e) {
alert("The input is not a valid Base64 message!");
}
}
Singleton Pattern
const Singleton = (function () {
let instance;
function createInstance() {
return { message: "This is the unique instance." };
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
const inst1 = Singleton.getInstance();
const inst2 = Singleton.getInstance();
console.log(inst1 === inst2);
Ensures that there is only one instance of an object and provides a global point of access.
Factory Pattern
function AnimalFactory(type) {
if (type === "dog") {
return { sound: () => "Woof!" };
} else if (type === "cat") {
return { sound: () => "Meow!" };
}
return { sound: () => "Unknown!" };
}
const animal = AnimalFactory("cat");
console.log(animal.sound());
Creates objects without specifying the exact class — useful for flexible instantiation.
Observer Pattern
class Subject {
constructor() {
this.observers = [];
}
add(observer) {
this.observers.push(observer);
}
notify(message) {
this.observers.forEach(obs => obs.update(message));
}
}
class Observer {
update(message) {
console.log("Received:", message);
}
}
const subject = new Subject();
const obs1 = new Observer();
subject.add(obs1);
subject.notify("Hello!");
Allows an object to notify other objects when its state changes.
Module Pattern and IIFE
This project demonstrates how to use an Immediately Invoked Function Expression (IIFE) to create a module with private variables and public methods.
Counter Module
const Counter = (function () {
let value = 0;
function updateDisplay() {
document.getElementById("counterValue").textContent = `Value: ${value}`;
}
return {
increment: function () {
value++;
updateDisplay();
},
decrement: function () {
value--;
updateDisplay();
}
};
})();
Value: 0
MVC Pattern Introduction
This project demonstrates the Model-View-Controller principle in JavaScript, an architecture that separates application logic into three distinct components:
- Model – manages the application's data and logic.
- View – displays the data to the user.
- Controller – mediates between Model and View, responding to user actions.
Counter with MVC Architecture
// 🔹 MODEL: manages the data
const Model = {
value: 0,
increment: function () {
this.value++;
},
decrement: function () {
this.value--;
},
getValue: function () {
return this.value;
}
};
// 🔹 VIEW: displays the data
const View = {
display: function (value) {
document.getElementById("view").textContent = `Value: ${value}`;
}
};
// 🔹 CONTROLLER: connects user actions to model and view
const Controller = {
increment: function () {
Model.increment(); // modify the data
View.display(Model.getValue()); // update the interface
},
decrement: function () {
Model.decrement();
View.display(Model.getValue());
}
};
// 🔹 Initialization: display the initial value
View.display(Model.getValue());
Value: 0
