A simple Task Manager built using HTML, CSS, and Vanilla JavaScript that allows users to create, edit, complete, and delete tasks while persisting data using Local Storage.
- Add new tasks
- Edit existing tasks
- Mark tasks as completed
- Delete tasks
- Categorize tasks
- Store data in Local Storage
- Persistent UI after page refresh
- Responsive and clean user interface
The application uses Local Storage to persist tasks.
let taskList = JSON.parse(localStorage.getItem('taskList')) || [];localStorage.getItem('taskList')retrieves previously stored tasks.JSON.parse()converts the JSON string back into a JavaScript array.|| []provides an empty array when no tasks exist.
Example:
[
{
taskTitle: "Learn JavaScript",
taskDescription: "Study DOM manipulation",
taskCategory: "Learning",
completed: false
}
]The ui() function is responsible for displaying all tasks on the screen.
let ui = () => {
taskList.forEach((task) => {
taskListContainer.innerHTML += `
<div class="task-item">
<h4>
${task.taskTitle}
<span class="category-tag">
${task.taskCategory}
</span>
</h4>
<p>${task.taskDescription}</p>
<span id="task-actions">
<button onClick="editTask(${taskList.indexOf(task)})">
Edit
</button>
<button onClick="markCompleted(${taskList.indexOf(task)})">
Completed
</button>
<button onClick="deleteTask(${taskList.indexOf(task)})">
Delete
</button>
</span>
</div>
`;
});
};- Loop through every task.
- Create a task card dynamically.
- Insert task details into HTML.
- Attach action buttons.
taskList.forEach((task, i) => {
if (task.completed) {
const cards =
taskListContainer.querySelectorAll('.task-item');
cards[i].style.background =
'rgba(99, 153, 34, 0.12)';
cards[i].style.borderColor =
'#c0dd97';
}
});- Detect completed tasks.
- Apply different styling.
- Provide visual feedback.
addBtn.addEventListener('click', () => {
taskFormSection.classList.remove('hidden');
});closeFormBtn.addEventListener('click', () => {
taskFormSection.classList.add('hidden');
});- Event Listeners
- Class Manipulation
- DOM Updates
let editTask = (index) => {
let task = taskList[index];
taskFormSection.classList.remove('hidden');
form[0].value = task.taskTitle;
form[1].value = task.taskDescription;
form[2].value = task.taskCategory;
taskList.splice(index, 1);
localStorage.setItem(
'taskList',
JSON.stringify(taskList)
);
taskListContainer.innerHTML = '';
ui();
};- Select task using index.
- Populate form fields.
- Remove old task temporarily.
- Allow user to update details.
- Save edited version again.
let markCompleted = (index) => {
taskList[index].completed = true;
localStorage.setItem(
'taskList',
JSON.stringify(taskList)
);
const cards =
taskListContainer.querySelectorAll('.task-item');
cards[index].style.background =
'rgba(99, 153, 34, 0.12)';
cards[index].style.borderColor =
'#c0dd97';
};- Locate task.
- Set completed property to true.
- Update Local Storage.
- Update UI styling.
let deleteTask = (index) => {
taskList.splice(index, 1);
localStorage.setItem(
'taskList',
JSON.stringify(taskList)
);
taskListContainer.innerHTML = '';
ui();
};- Remove task from array.
- Update Local Storage.
- Re-render UI.
form.addEventListener('submit', (e) => {
e.preventDefault();
let taskTitle = e.target[0].value;
let taskDescription = e.target[1].value;
let taskCategory = e.target[2].value;
let taskObject = {
taskTitle,
taskDescription,
taskCategory
};
taskList.push(taskObject);
localStorage.setItem(
'taskList',
JSON.stringify(taskList)
);
});if (
taskTitle === '' ||
taskDescription === '' ||
taskCategory === ''
) {
alert('Please fill all the fields');
return;
}if (
taskList.some(
task => task.taskTitle === taskTitle
)
) {
alert('Task with this title already exists');
return;
}- Prevent empty submissions.
- Prevent duplicate tasks.
- Improve data quality.
document.querySelector()
document.getElementById()
element.innerHTMLUsed to dynamically update the page.
addEventListener()Used for:
- Form submission
- Button clicks
- User interactions
localStorage.setItem()
localStorage.getItem()Used to persist task data.
JSON.stringify()
JSON.parse()Used to convert data between:
- JavaScript Objects
- JSON Strings
The browser converts code into visual content through the following stages:
HTML
↓
Parsing
↓
Tokenization
↓
DOM Tree
↓
CSSOM
↓
Render Tree
↓
Layout
↓
Paint
| Stage | Description |
|---|---|
| HTML | Browser receives HTML |
| Parsing | Reads document structure |
| Tokenization | Breaks content into tokens |
| DOM Tree | Creates document object model |
| CSSOM | Creates CSS object model |
| Render Tree | Combines DOM and CSSOM |
| Layout | Calculates positions and sizes |
| Paint | Draws pixels on screen |
Events travel through the DOM in three phases.
Capturing
↓
Target
↓
Bubbling
Example:
Grandparent
↓
Parent
↓
Child
↓
Button
Execution Order:
Capturing: Grandparent
Capturing: Parent
Capturing: Child
Target: Button
Bubbling: Child
Bubbling: Parent
Bubbling: Grandparent
element.addEventListener(
"click",
handler,
true
);true enables capturing.
element.addEventListener(
"click",
handler,
false
);false enables bubbling.
Defined in HTML.
<input value="Krishna">input.getAttribute("value");Output:
Krishna
Belongs to DOM object.
input.valueOutput:
Krishna
input.value =
"Changed via JS Property";Results:
input.getAttribute("value");
// Krishna
input.value;
// Changed via JS Property| Attribute | Property |
|---|---|
| Defined in HTML | Defined on DOM Object |
| Initial Value | Current Value |
| String Only | Any JavaScript Type |
| Accessed via getAttribute() | Accessed via dot notation |
| Doesn't automatically update | Updates dynamically |
- Search Tasks
- Filter by Category
- Sort by Date
- Dark Mode Toggle
- Drag and Drop Reordering
- Due Dates
- Priority Levels
- Toast Notifications
This project demonstrates practical usage of:
- DOM Manipulation
- Event Handling
- Form Validation
- Local Storage
- Dynamic Rendering
- Event Propagation
- Browser Rendering Pipeline
- Attributes vs Properties
A small project, but it touches many core JavaScript concepts that appear in interviews and real-world frontend development.