Comprehensive JavaScript Notes: From
Basics to Advanced
JavaScript is a versatile, high-level, and interpreted programming language primarily used for
creating interactive web pages. It's an essential technology for front-end web development, and
increasingly, with [Link], for back-end development as well.
1. Basic Concepts
1.1 Variables
Variables are containers for storing data values. JavaScript has three keywords for declaring
variables: var, let, and const.
● var: Oldest way to declare variables. Function-scoped and can be re-declared and
re-assigned.
var greeting = "Hello";
var greeting = "Hi"; // Re-declaration is allowed
greeting = "Hey"; // Re-assignment is allowed
[Link](greeting); // Output: Hey
● let: Block-scoped. Can be re-assigned but not re-declared within the same scope.
let name = "Alice";
// let name = "Bob"; // Error: Cannot re-declare block-scoped
variable 'name'
name = "Charlie"; // Re-assignment is allowed
[Link](name); // Output: Charlie
if (true) {
let blockScoped = "I'm inside a block";
[Link](blockScoped); // Output: I'm inside a block
}
// [Link](blockScoped); // Error: blockScoped is not defined
● const: Block-scoped. Cannot be re-assigned or re-declared. Must be initialized at
declaration.
const PI = 3.14159;
// PI = 3.14; // Error: Assignment to constant variable.
// const GRAVITY; // Error: Missing initializer in const
declaration
const person = { name: "David" };
[Link] = "Eve"; // Allowed: Modifying properties of an object
declared with const is fine
// person = { name: "Frank" }; // Error: Assignment to constant
variable.
[Link](person); // Output: { name: "Eve" }
1.2 Data Types
JavaScript has two main categories of data types: Primitive and Non-Primitive (Reference).
Primitive Data Types:
1. String: Represents text.
let message = "Hello, JavaScript!";
[Link](typeof message); // Output: string
2. Number: Represents both integers and floating-point numbers.
let age = 30;
let price = 99.99;
[Link](typeof age); // Output: number
[Link](typeof price); // Output: number
3. Boolean: Represents true or false.
let isActive = true;
let hasPermission = false;
[Link](typeof isActive); // Output: boolean
4. Undefined: A variable that has been declared but not yet assigned a value.
let undefinedVar;
[Link](undefinedVar); // Output: undefined
[Link](typeof undefinedVar); // Output: undefined
5. Null: Represents the intentional absence of any object value. It's a primitive value.
let emptyValue = null;
[Link](emptyValue); // Output: null
[Link](typeof emptyValue); // Output: object (This is a
long-standing bug in JavaScript)
6. Symbol (ES6): Represents a unique identifier.
const id1 = Symbol('id');
const id2 = Symbol('id');
[Link](id1 === id2); // Output: false
[Link](typeof id1); // Output: symbol
7. BigInt (ES2020): Represents whole numbers larger than 2^{53} - 1.
const bigNumber = 1234567890123456789012345678901234567890n;
[Link](typeof bigNumber); // Output: bigint
Non-Primitive (Reference) Data Types:
1. Object: A collection of key-value pairs.
let person = {
firstName: "John",
lastName: "Doe",
age: 30
};
[Link](typeof person); // Output: object
2. Array: A special type of object used to store ordered collections of data.
let colors = ["red", "green", "blue"];
[Link](typeof colors); // Output: object (Arrays are objects
in JS)
3. Function: A block of code designed to perform a particular task. Functions are also
objects.
function greet() {
[Link]("Hello!");
}
[Link](typeof greet); // Output: function (though technically
an object)
1.3 Operators
Operators perform operations on values and variables.
● Arithmetic Operators: +, -, *, /, % (modulus), ** (exponentiation), ++ (increment), --
(decrement)
let a = 10, b = 3;
[Link](a + b); // 13
[Link](a - b); // 7
[Link](a * b); // 30
[Link](a / b); // 3.333...
[Link](a % b); // 1
[Link](a ** b); // 1000 (10 to the power of 3)
a++; [Link](a); // 11
b--; [Link](b); // 2
● Assignment Operators: =, +=, -=, *=, /=, %=, **=
let x = 5;
x += 3; // x = x + 3; // x is now 8
[Link](x); // 8
● Comparison Operators: == (loose equality), === (strict equality), !=, !==, >, <, >=, <=
[Link](5 == '5'); // true (loose equality, type coercion)
[Link](5 === '5'); // false (strict equality, no type
coercion)
[Link](10 > 5); // true
● Logical Operators: && (AND), || (OR), ! (NOT)
let isAdult = true;
let hasLicense = false;
[Link](isAdult && hasLicense); // false
[Link](isAdult || hasLicense); // true
[Link](!isAdult); // false
● Ternary Operator: condition ? expr1 : expr2
let age = 18;
let status = (age >= 18) ? "Adult" : "Minor";
[Link](status); // Output: Adult
1.4 Control Flow
Control flow statements allow you to execute different blocks of code based on certain
conditions.
● if, else if, else:
let temperature = 25;
if (temperature > 30) {
[Link]("It's hot outside!");
} else if (temperature > 20) {
[Link]("It's pleasant.");
} else {
[Link]("It's cold.");
}
// Output: It's pleasant.
● switch:
let day = "Monday";
switch (day) {
case "Monday":
[Link]("Start of the week.");
break;
case "Friday":
[Link]("End of the work week!");
break;
default:
[Link]("Just another day.");
}
// Output: Start of the week.
1.5 Loops
Loops are used to execute a block of code repeatedly.
● for loop:
for (let i = 0; i < 5; i++) {
[Link]("Iteration " + i);
}
/*
Output:
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
*/
● while loop:
let count = 0;
while (count < 3) {
[Link]("Count: " + count);
count++;
}
/*
Output:
Count: 0
Count: 1
Count: 2
*/
● do...while loop: Executes the block once, then checks the condition.
let i = 0;
do {
[Link]("Do-while iteration: " + i);
i++;
} while (i < 0); // Condition is false, but runs once.
// Output: Do-while iteration: 0
● for...in loop: Iterates over enumerable properties of an object.
const car = { brand: "Toyota", model: "Camry", year: 2020 };
for (let key in car) {
[Link](`${key}: ${car[key]}`);
}
/*
Output:
brand: Toyota
model: Camry
year: 2020
*/
● for...of loop (ES6): Iterates over iterable objects (like Arrays, Strings, Maps, Sets, etc.).
const colors = ["red", "green", "blue"];
for (let color of colors) {
[Link](color);
}
/*
Output:
red
green
blue
*/
1.6 Functions
Functions are reusable blocks of code that perform a specific task.
● Function Declaration:
function greet(name) {
return "Hello, " + name + "!";
}
[Link](greet("Alice")); // Output: Hello, Alice!
● Function Expression:
const sayHi = function(name) {
return "Hi, " + name + "!";
};
[Link](sayHi("Bob")); // Output: Hi, Bob!
● Arrow Functions (ES6): Shorter syntax, no this binding, cannot be used as constructors.
const add = (a, b) => a + b;
[Link](add(5, 3)); // Output: 8
const multiply = (a, b) => {
// Multi-line arrow function needs return
return a * b;
};
[Link](multiply(4, 2)); // Output: 8
● Parameters and Arguments:
function calculateSum(num1, num2) { // num1, num2 are parameters
return num1 + num2;
}
[Link](calculateSum(10, 20)); // 10, 20 are arguments
● Default Parameters (ES6):
function welcome(name = "Guest") {
[Link](`Welcome, ${name}!`);
}
welcome("Charlie"); // Output: Welcome, Charlie!
welcome(); // Output: Welcome, Guest!
2. Intermediate Concepts
2.1 Arrays
Arrays are ordered lists of values.
● Creating Arrays:
let fruits = ["Apple", "Banana", "Cherry"];
let emptyArray = [];
let mixedArray = [1, "hello", true, { key: "value" }];
● Accessing Elements:
[Link](fruits[0]); // Output: Apple
[Link](fruits[[Link] - 1]); // Output: Cherry
● Modifying Elements:
fruits[1] = "Blueberry";
[Link](fruits); // Output: ["Apple", "Blueberry", "Cherry"]
● Common Array Methods:
○ push(): Adds element to the end.
○ pop(): Removes element from the end.
○ unshift(): Adds element to the beginning.
○ shift(): Removes element from the beginning.
○ splice(start, deleteCount, ...items): Adds/removes elements at any position.
○ slice(start, end): Returns a shallow copy of a portion of an array.
○ concat(): Joins arrays.
○ indexOf(), lastIndexOf(), includes(): Search for elements.
○ forEach(), map(), filter(), reduce(): Iteration and transformation.
○ find(), findIndex(): Find elements based on a condition.
<!-- end list -->let numbers = [1, 2, 3, 4, 5];
[Link](6); // [1, 2, 3, 4, 5, 6]
[Link](); // [1, 2, 3, 4, 5]
[Link](0); // [0, 1, 2, 3, 4, 5]
[Link](); // [1, 2, 3, 4, 5]
[Link](2, 1, 10); // Removes 3, adds 10: [1, 2, 10, 4, 5]
let subArray = [Link](1, 3); // [2, 10]
[Link](num => [Link](num * 2)); // Prints 2, 4, 20, 8,
10
let doubled = [Link](num => num * 2); // [2, 4, 20, 8, 10]
let evens = [Link](num => num % 2 === 0); // [2, 10, 4]
let sum = [Link]((acc, curr) => acc + curr, 0); // 22
2.2 Objects
Objects are collections of key-value pairs.
● Creating Objects:
let user = {
name: "Jane Doe",
age: 25,
email: "jane@[Link]",
isAdmin: false,
address: {
street: "123 Main St",
city: "Anytown"
},
greet: function() {
[Link](`Hello, my name is ${[Link]}`);
}
};
● Accessing Properties:
[Link]([Link]); // Dot notation: Jane Doe
[Link](user['age']); // Bracket notation: 25
[Link]([Link]); // Anytown
[Link](); // Hello, my name is Jane Doe
● Modifying/Adding Properties:
[Link] = 26;
[Link] = "555-1234";
[Link](user);
● Deleting Properties:
delete [Link];
[Link](user);
● Object Destructuring (ES6):
const { name, age } = user;
[Link](name, age); // Jane Doe 26
const { city } = [Link];
[Link](city); // Anytown
● Spread Operator (...) for Objects (ES2018):
const defaults = { color: "red", size: "M" };
const userPrefs = { size: "L", theme: "dark" };
const combined = { ...defaults, ...userPrefs };
[Link](combined); // { color: "red", size: "L", theme: "dark"
}
2.3 Prototypes and Prototypal Inheritance
JavaScript uses prototypal inheritance. Every object has a prototype, which is another object
that it inherits properties and methods from.
const animal = {
eats: true,
walk() {
[Link]("Animal walks.");
}
};
const rabbit = {
jumps: true,
__proto__: animal // rabbit inherits from animal
};
[Link]([Link]); // Output: true (inherited from animal)
[Link](); // Output: Animal walks. (inherited from
animal)
// Using [Link]()
const dog = [Link](animal);
[Link] = true;
[Link]([Link]); // true
2.4 this keyword
The this keyword refers to the object it belongs to. Its value depends on how the function is
called.
● Global context: this refers to the global object (window in browsers, global in [Link]).
● Method context: this refers to the object the method belongs to.
● Function context: In non-strict mode, this refers to the global object. In strict mode, this
is undefined.
● Event handlers: this refers to the element that received the event.
● Arrow functions: this is lexically scoped (it inherits this from the parent scope).
● call(), apply(), bind(): Explicitly set this.
<!-- end list -->
const myObject = {
value: 42,
getValue: function() {
[Link]([Link]); // `this` refers to myObject
}
};
[Link](); // Output: 42
const anotherObject = {
value: 100
};
// Using call to set `this` explicitly
[Link](anotherObject); // Output: 100
// Arrow function example
const arrowObject = {
name: "Arrow Guy",
sayName: function() {
// `this` here refers to arrowObject
const innerArrow = () => {
[Link]([Link]); // `this` here also refers to
arrowObject (lexical scope)
};
innerArrow();
}
};
[Link](); // Output: Arrow Guy
2.5 Closures
A closure is the combination of a function and the lexical environment within which that function
was declared. It allows a function to access variables from its outer scope even after the outer
function has finished executing.
function createCounter() {
let count = 0; // This variable is "closed over" by the returned
functions
return {
increment: function() {
count++;
[Link](count);
},
decrement: function() {
count--;
[Link](count);
},
getCount: function() {
return count;
}
};
}
const counter1 = createCounter();
[Link](); // Output: 1
[Link](); // Output: 2
[Link]([Link]()); // Output: 2
const counter2 = createCounter(); // New independent counter
[Link](); // Output: 1
2.6 Higher-Order Functions
Functions that take other functions as arguments or return functions as their result.
// Function that takes a function as an argument
function operateOnNumbers(a, b, operation) {
return operation(a, b);
}
function add(x, y) { return x + y; }
function subtract(x, y) { return x - y; }
[Link](operateOnNumbers(10, 5, add)); // Output: 15
[Link](operateOnNumbers(10, 5, subtract)); // Output: 5
// Function that returns a function
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
const multiplyBy5 = multiplier(5);
[Link](multiplyBy5(10)); // Output: 50
2.7 Event Loop and Asynchronous JavaScript
JavaScript is single-threaded, but it handles asynchronous operations (like network requests,
timers) using the Event Loop.
● Callbacks: Functions passed as arguments to be executed later.
[Link]("Start");
setTimeout(function() {
[Link]("Inside setTimeout (after 2 seconds)");
}, 2000);
[Link]("End");
// Output: Start -> End -> Inside setTimeout (after 2 seconds)
● Promises (ES6): Objects representing the eventual completion or failure of an
asynchronous operation.
const fetchData = new Promise((resolve, reject) => {
// Simulate an async operation
setTimeout(() => {
const success = true;
if (success) {
resolve("Data fetched successfully!");
} else {
reject("Failed to fetch data.");
}
}, 1500);
});
fetchData
.then(data => [Link](data)) // Output: Data fetched
successfully!
.catch(error => [Link](error))
.finally(() => [Link]("Promise finished."));
● Async/Await (ES2017): Syntactic sugar built on Promises, making asynchronous code
look synchronous.
async function getUserData() {
try {
[Link]("Fetching user data...");
const response = await
fetch('[Link]
const data = await [Link]();
[Link]("User data:", data);
} catch (error) {
[Link]("Error fetching user:", error);
}
}
getUserData();
Note: fetch API is a Web API, not part of core JavaScript, but commonly used with
async/await.
2.8 Error Handling
The try...catch...finally statement is used to handle errors gracefully.
function divide(a, b) {
try {
if (b === 0) {
throw new Error("Cannot divide by zero!");
}
return a / b;
} catch (error) {
[Link]("An error occurred:", [Link]);
return null; // Or re-throw, or return a default value
} finally {
[Link]("Division attempt finished.");
}
}
[Link](divide(10, 2)); // Output: Division attempt finished. \n 5
[Link](divide(10, 0)); // Output: An error occurred: Cannot
divide by zero! \n Division attempt finished. \n null
3. Advanced Concepts
3.1 ES6+ Features (Modern JavaScript)
● Classes: Syntactic sugar over JavaScript's existing prototypal inheritance.
class Animal {
constructor(name) {
[Link] = name;
}
speak() {
[Link](`${[Link]} makes a sound.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Call parent constructor
[Link] = breed;
}
speak() {
[Link](`${[Link]} (${[Link]}) barks.`);
}
}
const myDog = new Dog("Buddy", "Golden Retriever");
[Link](); // Output: Buddy (Golden Retriever) barks.
● Modules (import/export): Organize code into separate files for better maintainability and
reusability.
○ [Link]
// [Link]
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default function subtract(a, b) {
return a - b;
}
○ [Link]
// [Link]
import { PI, add } from './[Link]';
import subtractNumbers from './[Link]'; // Default import
[Link](PI); // 3.14159
[Link](add(2, 3)); // 5
[Link](subtractNumbers(5, 2)); // 3
○ Note: Modules require a server environment or specific browser configuration (e.g.,
<script type="module">).
● Spread (...) and Rest (...) Operators:
○ Spread: Expands an iterable (like an array or string) into individual elements.
const arr1 = [1, 2];
const arr2 = [3, 4];
const combinedArray = [...arr1, ...arr2, 5]; // [1, 2, 3, 4,
5]
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const combinedObject = { ...obj1, ...obj2 }; // { a: 1, b: 2,
c: 3, d: 4 }
function sum(x, y, z) { return x + y + z; }
const numbers = [1, 2, 3];
[Link](sum(...numbers)); // 6
○ Rest: Collects remaining arguments into an array.
function collectArgs(first, ...rest) {
[Link](first); // 1
[Link](rest); // [2, 3, 4, 5]
}
collectArgs(1, 2, 3, 4, 5);
● Template Literals (Template Strings): Backticks (`) for embedded expressions and
multi-line strings.
const name = "World";
const greeting = `Hello, ${name}!
This is a multi-line string.`;
[Link](greeting);
● Destructuring Assignment: Extract values from arrays or properties from objects into
distinct variables.
// Array Destructuring
const [first, second, ...restOfArray] = [10, 20, 30, 40, 50];
[Link](first); // 10
[Link](second); // 20
[Link](restOfArray); // [30, 40, 50]
// Object Destructuring (already covered)
● Default Parameters: (Already covered in Functions)
3.2 Generators
Functions that can be paused and resumed, yielding (returning) multiple values over time. They
are defined with function*.
function* idGenerator() {
let id = 1;
while (true) {
yield id++; // Pause execution and return id, resume on next()
}
}
const gen = idGenerator();
[Link]([Link]().value); // 1
[Link]([Link]().value); // 2
[Link]([Link]().value); // 3
3.3 Proxies and Reflect (ES6)
● Proxies: Objects that wrap another object or function and can intercept fundamental
operations (e.g., property lookup, assignment, function invocation).
const target = {
message1: "hello",
message2: "world"
};
const handler = {
get: function(target, property, receiver) {
if (property === 'message2') {
return 'Intercepted message2!';
}
return [Link](target, property, receiver);
},
set: function(target, property, value, receiver) {
if (property === 'message1' && typeof value !== 'string')
{
throw new TypeError('message1 must be a string');
}
return [Link](target, property, value, receiver);
}
};
const proxy = new Proxy(target, handler);
[Link](proxy.message1); // hello
[Link](proxy.message2); // Intercepted message2!
proxy.message1 = "new hello";
[Link](proxy.message1); // new hello
// proxy.message1 = 123; // Throws TypeError
● Reflect: A built-in object that provides methods for interceptable JavaScript operations.
Often used with Proxies.
3.4 Web APIs (Browser Environment)
These are not part of core JavaScript but are crucial for web development.
● DOM Manipulation (Document Object Model): Interface for HTML and XML documents.
// Select elements
const myDiv = [Link]('myDiv');
const paragraphs = [Link]('.my-paragraph');
// Create elements
const newElement = [Link]('p');
[Link] = "This is a new paragraph.";
[Link]('dynamic-text');
// Append elements
[Link](newElement);
// Modify attributes/styles
[Link] = 'lightblue';
[Link]('data-info', 'important');
// Event Listeners
const myButton = [Link]('myButton');
[Link]('click', () => {
// In a real app, use a custom modal or message box instead of
alert()
// For this example, we'll use a placeholder for brevity.
[Link]('Button clicked!');
});
● Fetch API: For making network requests (e.g., to APIs).
// Already demonstrated in Async/Await section.
// fetch(url, options) returns a Promise.
● Local Storage and Session Storage: Store data in the browser.
○ localStorage: Data persists even after browser close.
○ sessionStorage: Data cleared when the browser tab/window is closed. <!-- end list
-->
// Local Storage
[Link]('username', 'Alice');
const username = [Link]('username');
[Link](username); // Alice
[Link]('username');
// [Link](); // Clears all items
// Session Storage
[Link]('tempData', [Link]({ id: 1, value:
'temporary' }));
const tempData = [Link]([Link]('tempData'));
[Link](tempData); // { id: 1, value: 'temporary' }
3.5 Design Patterns
Common solutions to recurring problems in software design.
● Module Pattern: Encapsulate private state and public methods.
const ShoppingCart = (function() {
let items = []; // Private variable
function addItem(item) {
[Link](item);
[Link](`${item} added.`);
}
function removeItem(item) {
const index = [Link](item);
if (index > -1) {
[Link](index, 1);
[Link](`${item} removed.`);
}
}
function getItems() {
return [...items]; // Return a copy to prevent external
modification
}
return {
add: addItem,
remove: removeItem,
list: getItems
};
})();
[Link]("Laptop");
[Link]("Mouse");
[Link]([Link]()); // ["Laptop", "Mouse"]
// [Link]([Link]); // undefined (private)
● Revealing Module Pattern: A variation of the module pattern where you explicitly return
an object with pointers to the private functions you want to expose.
const Calculator = (function() {
let result = 0;
function add(num) {
result += num;
}
function subtract(num) {
result -= num;
}
function getResult() {
return result;
}
return {
add: add,
subtract: subtract,
getResult: getResult
};
})();
[Link](10);
[Link](3);
[Link]([Link]()); // 7
● Singleton Pattern: Ensures a class has only one instance and provides a global point of
access to it.
const Logger = (function() {
let instance;
function init() {
let logs = [];
function addLog(message) {
const timestamp = new Date().toISOString();
[Link](`[${timestamp}] ${message}`);
}
function getLogs() {
return logs;
}
return {
addLog: addLog,
getLogs: getLogs
};
}
return {
getInstance: function() {
if (!instance) {
instance = init();
}
return instance;
}
};
})();
const logger1 = [Link]();
[Link]("First message.");
const logger2 = [Link]();
[Link]("Second message.");
[Link]([Link]()); // Both messages are present, as
it's the same instance.
[Link]([Link]());
● Observer Pattern (Publish/Subscribe): Defines a one-to-many dependency between
objects so that when one object changes state, all its dependents are notified and
updated automatically.
class Subject {
constructor() {
[Link] = [];
}
addObserver(observer) {
[Link](observer);
}
removeObserver(observer) {
[Link] = [Link](obs => obs !==
observer);
}
notify(data) {
[Link](observer => [Link](data));
}
}
class Observer {
constructor(name) {
[Link] = name;
}
update(data) {
[Link](`${[Link]} received update: ${data}`);
}
}
const newsFeed = new Subject();
const subscriber1 = new Observer("Subscriber A");
const subscriber2 = new Observer("Subscriber B");
[Link](subscriber1);
[Link](subscriber2);
[Link]("Breaking News: JavaScript is awesome!");
// Output:
// Subscriber A received update: Breaking News: JavaScript is
awesome!
// Subscriber B received update: Breaking News: JavaScript is
awesome!
[Link](subscriber1);
[Link]("Another update!");
// Output:
// Subscriber B received update: Another update!
3.6 Web Workers
Allows running JavaScript in the background thread, separate from the main execution thread,
preventing UI blocking.
// [Link] (or script in HTML)
// This part would typically be in your main HTML script
/*
if ([Link]) {
const myWorker = new Worker('[Link]'); // Assuming [Link] is
a separate file
[Link]({ command: 'startCalculation', data:
1000000000 });
[Link] = function(e) {
[Link]('Message received from worker:', [Link]);
};
[Link] = function(error) {
[Link]('Worker error:', error);
};
[Link]('Main thread continues...');
} else {
[Link]('Web Workers are not supported in this browser.');
}
*/
// [Link] content (this would be in a separate file named
[Link])
/*
onmessage = function(e) {
if ([Link] === 'startCalculation') {
let sum = 0;
for (let i = 0; i < [Link]; i++) {
sum += i;
}
postMessage(sum); // Send result back to main thread
}
};
*/
Note: Web Workers cannot directly access the DOM. The above code snippets for Web
Workers are illustrative and would typically reside in separate files ([Link] and [Link]) in a
real project.
4. Real-World Project: Simple To-Do List Application
This project will demonstrate several concepts:
● DOM Manipulation
● Event Handling
● Local Storage for persistence
● Basic Array methods
● Functions
The application will allow users to add, delete, and mark tasks as complete, with tasks persisting
across browser sessions.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
<title>Simple To-Do List</title>
<!-- Tailwind CSS CDN for easy styling -->
<script src="[Link]
<link
href="[Link]
display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #f0f2f5;
display: flex;
justify-content: center;
align-items: flex-start; /* Align to top for better
content flow */
min-height: 100vh;
padding: 20px;
box-sizing: border-box;
}
.container {
background-color: #ffffff;
border-radius: 1rem; /* Rounded corners */
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
padding: 2rem;
width: 100%;
max-width: 500px;
margin-top: 2rem; /* Add some margin from the top */
}
.task-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem 0;
border-bottom: 1px solid #e5e7eb;
}
.task-item:last-child {
border-bottom: none;
}
.task-text {
flex-grow: 1;
margin-right: 1rem;
word-break: break-word; /* Ensure long words wrap */
}
.[Link] {
text-decoration: line-through;
color: #6b7280;
}
.action-buttons button {
padding: 0.4rem 0.8rem;
border-radius: 0.5rem;
font-size: 0.875rem;
font-weight: 600;
transition: background-color 0.2s ease-in-out;
}
.action-buttons .complete-btn {
background-color: #d1fae5; /* Light green */
color: #065f46; /* Dark green text */
}
.action-buttons .complete-btn:hover {
background-color: #a7f3d0;
}
.action-buttons .delete-btn {
background-color: #fee2e2; /* Light red */
color: #991b1b; /* Dark red text */
margin-left: 0.5rem;
}
.action-buttons .delete-btn:hover {
background-color: #fecaca;
}
.input-group {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
}
.input-group input {
flex-grow: 1;
padding: 0.75rem 1rem;
border: 1px solid #d1d5db;
border-radius: 0.75rem;
outline: none;
transition: border-color 0.2s;
}
.input-group input:focus {
border-color: #3b82f6; /* Blue focus ring */
}
.input-group button {
background-color: #3b82f6; /* Blue */
color: white;
padding: 0.75rem 1.5rem;
border-radius: 0.75rem;
font-weight: 600;
transition: background-color 0.2s ease-in-out;
cursor: pointer;
}
.input-group button:hover {
background-color: #2563eb; /* Darker blue */
}
.message-box {
background-color: #fff;
border-radius: 0.75rem;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
padding: 1rem;
margin-top: 1rem;
text-align: center;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1000;
display: none; /* Hidden by default */
max-width: 300px;
}
.message-box button {
background-color: #3b82f6;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
margin-top: 0.75rem;
cursor: pointer;
}
.[Link] {
display: block;
}
</style>
</head>
<body class="antialiased">
<div class="container">
<h1 class="text-3xl font-bold text-center text-gray-800
mb-6">My To-Do List</h1>
<div class="input-group">
<input type="text" id="taskInput" placeholder="Add a new
task..." class="focus:ring-2 focus:ring-blue-500">
<button id="addTaskBtn">Add Task</button>
</div>
<ul id="taskList" class="divide-y divide-gray-200">
<!-- Tasks will be dynamically added here -->
</ul>
</div>
<!-- Custom Message Box -->
<div id="messageBox" class="message-box">
<p id="messageBoxText"></p>
<button id="messageBoxCloseBtn">OK</button>
</div>
<script>
// Get DOM elements
const taskInput = [Link]('taskInput');
const addTaskBtn = [Link]('addTaskBtn');
const taskList = [Link]('taskList');
const messageBox = [Link]('messageBox');
const messageBoxText =
[Link]('messageBoxText');
const messageBoxCloseBtn =
[Link]('messageBoxCloseBtn');
// Array to store tasks
// Each task object will have: { id: string, text: string,
completed: boolean }
let tasks = [];
/**
* Displays a custom message box instead of alert().
* @param {string} message - The message to display.
*/
function showMessageBox(message) {
[Link] = message;
[Link]('show');
}
/**
* Hides the custom message box.
*/
function hideMessageBox() {
[Link]('show');
}
// Event listener for the message box close button
[Link]('click', hideMessageBox);
/**
* Loads tasks from local storage when the page loads.
*/
function loadTasks() {
const storedTasks = [Link]('tasks');
if (storedTasks) {
tasks = [Link](storedTasks);
renderTasks();
}
}
/**
* Saves the current tasks array to local storage.
*/
function saveTasks() {
[Link]('tasks', [Link](tasks));
}
/**
* Renders all tasks from the `tasks` array to the DOM.
*/
function renderTasks() {
[Link] = ''; // Clear existing list items
if ([Link] === 0) {
[Link] = '<li class="text-center
text-gray-500 py-4">No tasks yet. Add one!</li>';
return;
}
[Link](task => {
const listItem = [Link]('li');
[Link]('task-item');
[Link]('data-id', [Link]); // Store
task ID on the element
// Task text span
const taskTextSpan = [Link]('span');
[Link]('task-text',
'text-gray-700', 'text-lg');
[Link] = [Link];
if ([Link]) {
[Link]('completed');
}
// Action buttons container
const actionButtonsDiv =
[Link]('div');
[Link]('action-buttons');
// Complete button
const completeBtn = [Link]('button');
[Link]('complete-btn');
[Link] = [Link] ? 'Undo' :
'Complete';
[Link]('click', () =>
toggleComplete([Link]));
// Delete button
const deleteBtn = [Link]('button');
[Link]('delete-btn');
[Link] = 'Delete';
[Link]('click', () =>
deleteTask([Link]));
// Append elements
[Link](completeBtn);
[Link](deleteBtn);
[Link](taskTextSpan);
[Link](actionButtonsDiv);
[Link](listItem);
});
}
/**
* Adds a new task to the list.
*/
function addTask() {
const taskText = [Link](); // Get text and
remove whitespace
if (taskText === '') {
showMessageBox('Task cannot be empty!');
return;
}
const newTask = {
id: [Link](), // Generate a unique ID for
the task
text: taskText,
completed: false
};
[Link](newTask);
saveTasks(); // Save to local storage
renderTasks(); // Re-render the list
[Link] = ''; // Clear input field
}
/**
* Toggles the 'completed' status of a task.
* @param {string} id - The ID of the task to toggle.
*/
function toggleComplete(id) {
tasks = [Link](task =>
[Link] === id ? { ...task, completed: ![Link]
} : task
);
saveTasks();
renderTasks();
}
/**
* Deletes a task from the list.
* @param {string} id - The ID of the task to delete.
*/
function deleteTask(id) {
tasks = [Link](task => [Link] !== id);
saveTasks();
renderTasks();
}
// Event listener for Add Task button click
[Link]('click', addTask);
// Event listener for Enter key press in the input field
[Link]('keypress', (event) => {
if ([Link] === 'Enter') {
addTask();
}
});
// Load tasks when the page first loads
[Link]('DOMContentLoaded', loadTasks);
</script>
</body>
</html>