July 01, 2025 • 5 min read
đź“‹Building a To-Do List with a Date Picker đź“… using HTML, CSS, and JavaScript
A to-do list is a simple yet effective tool to manage daily tasks efficiently. In this tutorial, we will create a to-do list with a date picker using HTML, CSS, and JavaScript
✨ Features of the to-do List app
- Adding tasks with a due date.
- Storing tasks in local storage for persistence.
- Displaying tasks with a countdown of days left.
- Marking tasks as completed.
- Showing overdue tasks.
- Deleting individual tasks or clearing all tasks at once.
đź§± Project Structure
/to-do-list-app
├── index.html
├── styles.css
└── script.js
🖼️ HTML Code: to-do list app
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>To-Do List</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>To-Do List</h1>
<div class="input-container">
<input type="text" id="task-input" placeholder="Enter your task...">
<div class="date-picker">
<span class="calendar-icon" id="calendar-icon">đź“…</span>
<input type="date" id="task-date" class="hidden" />
</div>
<button id="add-task-btn">+</button>
</div>
<div id="task-list"></div>
<!-- Bottom section for pending tasks and clear all button -->
<div id="footer">
<p id="pending-tasks">You have 0 pending tasks.</p>
<button id="clear-all-btn" style="display: none;">Clear All</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Explanation:
- The
input-containerholds an input field for the task, a calendar icon with a date picker, and an add button. - The
task-listdiv will dynamically display added tasks. - The
footerdisplays the number of pending tasks and a “Clear All” button.
🎨 CSS Code: Styling for to-do list app
:root {
--gradient: linear-gradient(130deg, #ef893b, #e75c2c);
--gradient-hover: linear-gradient(90deg, #e75c2c, #ef893b);
}
body {
font-family: Arial, sans-serif;
background-image: var(--gradient);
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
width: 550px;
padding: 20px;
background: #2c3036;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
text-align: center;
}
h1 {
margin-bottom: 20px;
color: #898f97;
}
.input-container {
display: flex;
justify-content: center;
margin-bottom: 20px;
align-items: center;
gap: 10px;
}
input {
padding: 10px;
border-radius: 5px;
background-color: #363a41;
font-size: 16px;
}
input[type=text] {
flex-grow: 1;
padding: 10px;
border-radius: 5px;
background-color: #363a41;
font-size: 16px;
color: #f6f6f6;
}
input:focus {
outline: none;
}
input.edit-date {
background-color: #ccc;
}
button {
padding: 10px 14px;
border: none;
background: var(--gradient);
color: white;
font-size: 20px;
cursor: pointer;
font-weight: 600;
border-radius: 5px;
}
button:hover {
background: var(--gradient-hover);
}
ul {
list-style: none;
padding: 0;
}
li {
display: flex;
justify-content: space-between;
align-items: center;
background: #131827;
padding: 10px;
margin: 5px 0;
border-radius: 5px;
}
li span {
flex: 1;
color: #898f97;
margin-left: 10px;
text-align: left;
}
.task-checkbox {
appearance: none;
width: 16px;
height: 16px;
border: 2px solid #898f97;
border-radius: 50%;
cursor: pointer;
position: relative;
}
.task-checkbox:checked {
background-color: #28a745;
border-color: #28a745;
}
.task-checkbox:checked::after {
content: 'âś”';
color: white;
font-size: 14px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.delete-btn {
background: none;
border: none;
cursor: pointer;
font-size: 18px;
color: red;
}
.delete-btn:hover {
color: darkred;
background: none;
}
.edit-btn, .edit-btn:hover,
.save-btn, .save-btn:hover
{
background: none;
}
.completed {
text-decoration: line-through;
color: gray;
}
.task-checkbox {
margin-right: 10px;
cursor: pointer;
}
#pending-tasks {
margin-top: 15px;
font-size: 16px;
color: #898f97;
font-weight: bold;
text-align: center;
}
.hidden {
position: absolute;
opacity: 0;
pointer-events: none
}
.days-left {
font-size: 14px;
color: #ff5733;
}
#footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 15px;
font-size: 16px;
color: #333;
font-weight: bold;
}
#clear-all-btn {
padding: 8px 12px;
background: red;
color: white;
border: none;
cursor: pointer;
font-size: 14px;
border-radius: 5px;
display: none;
}
#clear-all-btn:hover {
background: darkred;
}
.date-picker {
position: relative;
width: 40px;
height: 40px;
background: #363a41;
border-radius: 5px;
cursor: pointer;
display: inline-block;
text-align: center;
}
.calendar-icon {
font-size: 20px;
color: white;
cursor: pointer;
line-height: 40px;
}
Explanation:
- The
.input-containeris styled for proper alignment. .hiddenclass hides the default date picker but keeps it functional..days-leftis used to display the remaining days dynamically.
đź§ JavaScript Functionality
document.addEventListener("DOMContentLoaded", loadTasks);
const taskInput = document.getElementById("task-input");
const taskDateInput = document.getElementById("task-date");
const calendarIcon = document.getElementById("calendar-icon");
const addTaskBtn = document.getElementById("add-task-btn");
const taskList = document.getElementById("task-list");
const pendingTaskCount = document.getElementById("pending-tasks");
const clearAllBtn = document.getElementById("clear-all-btn");
let selectedDate = "";
// Show date picker when clicking the calendar icon
calendarIcon.addEventListener("click", () => taskDateInput.showPicker());
// Capture selected date
taskDateInput.addEventListener("change", (e) => {
selectedDate = e.target.value;
});
addTaskBtn.addEventListener("click", addTask);
clearAllBtn.addEventListener("click", clearAllTasks);
function addTask() {
const taskText = taskInput.value.trim();
if (taskText === "" || selectedDate === "") {
alert("Please enter a task and select a due date.");
return;
}
const taskItem = createTaskElement(taskText, false, selectedDate);
taskList.appendChild(taskItem);
saveTaskToLocalStorage(taskText, false, selectedDate);
taskInput.value = "";
selectedDate = "";
updatePendingTasks();
}
function createTaskElement(taskText, isCompleted, taskDate) {
const li = document.createElement("li");
const daysLeft = calculateDaysLeft(taskDate);
const daysLeftText = daysLeft === 0 ? "Task Overdue" : `${daysLeft} ${daysLeft === 1 ? "day" : "days"} left`;
let color = daysLeft < 3 ? "red" : daysLeft <= 5 ? "orange" : "green";
li.innerHTML = `
<input type="checkbox" class="task-checkbox" ${isCompleted ? "checked" : ""}>
<span class="task-text ${isCompleted ? "completed" : ""}">${taskText}</span>
<span class="days-left" style="color: ${color};">${daysLeftText}</span>
<button class="edit-btn">✏️</button>
<button class="delete-btn">❌</button>
`;
const editBtn = li.querySelector(".edit-btn");
const deleteBtn = li.querySelector(".delete-btn");
const taskTextElement = li.querySelector(".task-text");
li.querySelector(".task-checkbox").addEventListener("change", (e) => {
const isChecked = e.target.checked;
taskTextElement.classList.toggle("completed", isChecked);
updateTaskStatus(taskText, isChecked, taskDate);
updatePendingTasks();
});
editBtn.addEventListener("click", () => editTask(li, taskText, taskDate));
deleteBtn.addEventListener("click", () => {
removeTaskFromLocalStorage(taskText);
li.remove();
updatePendingTasks();
});
return li;
}
function editTask(li, oldText, oldDate) {
const taskTextElement = li.querySelector(".task-text");
const daysLeftElement = li.querySelector(".days-left");
const editBtn = li.querySelector(".edit-btn");
// Create an input field for editing the task text
const textInput = document.createElement("input");
textInput.type = "text";
textInput.value = taskTextElement.textContent;
textInput.classList.add("edit-input");
// Create a date picker for editing the due date
const dateInput = document.createElement("input");
dateInput.type = "date";
dateInput.value = oldDate;
dateInput.classList.add("edit-date");
// Create a save button
const saveBtn = document.createElement("button");
saveBtn.innerHTML = "đź’ľ";
saveBtn.classList.add("save-btn");
// Replace elements with input fields
li.replaceChild(textInput, taskTextElement);
li.replaceChild(dateInput, daysLeftElement);
li.replaceChild(saveBtn, editBtn);
saveBtn.addEventListener("click", () => saveEditedTask(li, oldText, textInput.value, dateInput.value));
}
function saveEditedTask(li, oldText, newText, newDate) {
if (!newText.trim() || !newDate) {
alert("Task name and date cannot be empty.");
return;
}
const daysLeft = calculateDaysLeft(newDate);
const daysLeftText = daysLeft === 0 ? "Task Overdue" : `${daysLeft} ${daysLeft === 1 ? "day" : "days"} left`;
let color = daysLeft < 3 ? "red" : daysLeft <= 5 ? "orange" : "green";
// Restore task text and days left elements
const taskTextElement = document.createElement("span");
taskTextElement.textContent = newText;
taskTextElement.classList.add("task-text");
const daysLeftElement = document.createElement("span");
daysLeftElement.textContent = daysLeftText;
daysLeftElement.style.color = color;
daysLeftElement.classList.add("days-left");
const editBtn = document.createElement("button");
editBtn.innerHTML = "✏️";
editBtn.classList.add("edit-btn");
// Restore elements in the list item
li.replaceChild(taskTextElement, li.querySelector(".edit-input"));
li.replaceChild(daysLeftElement, li.querySelector(".edit-date"));
li.replaceChild(editBtn, li.querySelector(".save-btn"));
editBtn.addEventListener("click", () => editTask(li, newText, newDate));
updateTaskInLocalStorage(oldText, newText, newDate);
loadTasks(); // Reload sorted tasks
}
function calculateDaysLeft(taskDate) {
const today = new Date();
const dueDate = new Date(taskDate);
const timeDiff = dueDate - today;
const daysLeft = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
return daysLeft < 0 ? 0 : daysLeft;
}
function saveTaskToLocalStorage(taskText, isCompleted, taskDate) {
let tasks = JSON.parse(localStorage.getItem("tasks")) || [];
tasks.push({ text: taskText, completed: isCompleted, date: taskDate });
localStorage.setItem("tasks", JSON.stringify(tasks));
}
function updateTaskInLocalStorage(oldText, newText, newDate) {
let tasks = JSON.parse(localStorage.getItem("tasks")) || [];
tasks = tasks.map(task =>
task.text === oldText ? { text: newText, completed: task.completed, date: newDate } : task
);
localStorage.setItem("tasks", JSON.stringify(tasks));
}
function removeTaskFromLocalStorage(taskText) {
let tasks = JSON.parse(localStorage.getItem("tasks")) || [];
tasks = tasks.filter(task => task.text !== taskText);
localStorage.setItem("tasks", JSON.stringify(tasks));
}
function loadTasks() {
let tasks = JSON.parse(localStorage.getItem("tasks")) || [];
tasks.sort((a, b) => calculateDaysLeft(a.date) - calculateDaysLeft(b.date));
taskList.innerHTML = "";
tasks.forEach(task => {
const taskItem = createTaskElement(task.text, task.completed, task.date);
taskList.appendChild(taskItem);
});
updatePendingTasks();
}
function updatePendingTasks() {
let tasks = JSON.parse(localStorage.getItem("tasks")) || [];
let pendingCount = tasks.filter(task => !task.completed).length;
pendingTaskCount.textContent = `You have ${pendingCount} pending tasks.`;
clearAllBtn.style.display = tasks.length > 0 ? "inline-block" : "none";
}
clearAllBtn.addEventListener("click", clearAllTasks);
function clearAllTasks() {
localStorage.removeItem("tasks"); // Clear tasks from local storage
taskList.innerHTML = ""; // Clear all tasks from the DOM
updatePendingTasks(); // Update the pending tasks count
}
Explanation:
calculateDaysLeftcomputes the days remaining and shows “Task Overdue” when necessary.- Tasks persist in local storage.
- A delete button is added to remove tasks.
Conclusion
This to-do list effectively manages tasks with due dates, highlights overdue tasks, and persists data using local storage. Try customizing it further to enhance its functionality!
Happy coding!