├── index.html ├── script.js └── style.css /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Todo App 9 | 10 | 11 |
12 |

Todo App

13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |
21 | 22 | 26 |
27 | 45 |
46 |
47 |
48 | 49 | 53 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | const labels = document.querySelectorAll(".add-wrapper label"); 2 | 3 | labels.forEach((label) => { 4 | let input = label.querySelector("input"); 5 | let span = label.querySelector("span"); 6 | input.addEventListener("change", () => { 7 | span.innerHTML = input.value; 8 | }); 9 | }); 10 | 11 | let tasksArr = [ 12 | { 13 | title: "Task 1", 14 | description: "Task 1 description", 15 | date: "23 Dec 2022", 16 | time: "10:10", 17 | }, 18 | { 19 | title: "Task 2", 20 | description: "Task 2 description", 21 | date: "23 Dec 2022", 22 | time: "10:10", 23 | }, 24 | { 25 | title: "Task 3", 26 | description: "Task 3 description", 27 | date: "23 Dec 2022", 28 | time: "10:10", 29 | }, 30 | { 31 | title: "Task 4", 32 | description: "Task 4 description", 33 | date: "23 Dec 2021", 34 | time: "10:10", 35 | }, 36 | ]; 37 | const tasksWrapper = document.querySelector(".tasks-wrapper"); 38 | 39 | function renderTasks() { 40 | tasksWrapper.innerHTML = ""; 41 | 42 | //taskArray empty 43 | if (tasksArr.length === 0) { 44 | tasksWrapper.innerHTML = `
No tasks, Add one now
`; 45 | return; 46 | } 47 | 48 | //if tasks arr has tasks 49 | 50 | tasksArr.forEach((task) => { 51 | //check if expired 52 | let expired; 53 | expired = checkExpired(task) ? "expired" : ""; 54 | 55 | tasksWrapper.innerHTML += ` 56 |
57 |
58 |
59 | 60 |
61 |
62 |
63 |

${task.title}

64 |

${task.description}

65 |
66 |

67 | 68 | ${task.date} 69 |

70 |

71 | 72 |

73 |

74 | 75 | ${task.time} 76 |

77 |
78 |
79 |
80 | `; 81 | }); 82 | 83 | tasksWrapper.innerHTML += ` 84 |
85 | 86 |
`; 87 | 88 | //add event listners 89 | 90 | const tasks = document.querySelectorAll(".task"); 91 | 92 | tasks.forEach((task) => { 93 | task.addEventListener("click", (e) => { 94 | //if radio clicked 95 | if (e.target.classList.contains("radio")) { 96 | task.classList.toggle("selected"); 97 | //show delete button when at leaset one taske selected 98 | if (document.querySelector(".task.selected")) { 99 | document.querySelector(".delete").classList.add("show"); 100 | } else { 101 | document.querySelector(".delete").classList.remove("show"); 102 | } 103 | } 104 | }); 105 | }); 106 | 107 | //on delete remove task from arr and rerender 108 | const deleteBtn = document.querySelector(".delete"); 109 | deleteBtn.addEventListener("click", deleteTasks); 110 | } 111 | 112 | renderTasks(); 113 | 114 | function checkExpired(task) { 115 | let date = new Date(task.date); 116 | let time = new Date(task.time); 117 | let now = new Date(); 118 | if (date < now || time < now) { 119 | return true; 120 | } 121 | return false; 122 | 123 | //true if current date or time is less means task has to come yet 124 | } 125 | 126 | function deleteTasks() { 127 | const selectedTasks = document.querySelectorAll(".task.selected"); 128 | if (selectedTasks.length === 0) return; 129 | //if some taks selected 130 | let confirmDelete = confirm("Are you sure you want to delete selected tasks"); 131 | if (confirmDelete) { 132 | selectedTasks.forEach((task) => { 133 | //get title of task and filter matching title tasks 134 | let title = task.querySelector(".title").innerHTML; 135 | tasksArr = tasksArr.filter((task) => task.title !== title); 136 | }); 137 | renderTasks(); 138 | } 139 | } 140 | 141 | const addTaskForm = document.getElementById("add-task-form"), 142 | titleElem = document.getElementById("title"), 143 | descriptionElem = document.getElementById("description"), 144 | dateElem = document.getElementById("date"), 145 | timeElem = document.getElementById("time"); 146 | 147 | addTaskForm.addEventListener("submit", (e) => { 148 | e.preventDefault(); 149 | let title = titleElem.value, 150 | description = descriptionElem.value, 151 | date = dateElem.value, 152 | time = timeElem.value; 153 | //validate 154 | if (title === "" || description === "" || date === "" || time === "") { 155 | //if anything empty 156 | alert("please fill all the fields"); 157 | } 158 | 159 | let task = { 160 | title, 161 | description, 162 | date, 163 | time, 164 | }; 165 | 166 | //push in arr 167 | tasksArr.push(task); 168 | //rerender arr 169 | renderTasks(); 170 | //clear after adding 171 | clear(); 172 | }); 173 | 174 | function clear() { 175 | titleElem.value = ""; 176 | descriptionElem.value = ""; 177 | dateElem.value = ""; 178 | timeElem.value = ""; 179 | 180 | dateElem.nextElementSibling.innerHTML = "due date"; 181 | timeElem.nextElementSibling.innerHTML = "due time"; 182 | } 183 | //clear on clear btn 184 | const clearBtn = document.querySelector(".clear"); 185 | 186 | clearBtn.addEventListener("click", clear); 187 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Poppins:100,100italic,200,200italic,300,300italic,regular,italic,500,500italic,600,600italic,700,700italic,800,800italic,900,900italic); 2 | :root { 3 | --primary-color: #672ee3; 4 | } 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | border: 0; 9 | outline: 0; 10 | box-sizing: border-box; 11 | font-family: "poppins", sans-serif; 12 | } 13 | body { 14 | min-height: 100vh; 15 | display: flex; 16 | align-items: center; 17 | justify-content: center; 18 | background-color: #efeef5; 19 | } 20 | .container { 21 | position: relative; 22 | width: 600px; 23 | min-height: 300px; 24 | display: flex; 25 | flex-direction: column; 26 | align-items: center; 27 | background-color: #fff; 28 | border-radius: 10px; 29 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 30 | } 31 | .container .heading { 32 | width: 100%; 33 | padding: 20px; 34 | text-align: center; 35 | font-size: 18px; 36 | font-weight: 500; 37 | border-bottom: 1px solid #eee; 38 | } 39 | .tasks-wrapper { 40 | width: 100%; 41 | padding: 20px; 42 | display: flex; 43 | flex-direction: column; 44 | gap: 20px; 45 | height: 300px; 46 | overflow: auto; 47 | } 48 | .task { 49 | display: flex; 50 | gap: 20px; 51 | padding: 0 10px 20px 10px; 52 | border-bottom: 1px solid #eee; 53 | } 54 | .task:last-child { 55 | border-bottom: none; 56 | } 57 | .task .radio { 58 | position: relative; 59 | display: block; 60 | width: 20px; 61 | height: 20px; 62 | border-radius: 50%; 63 | margin-top: 5px; 64 | border: 1px solid #eee; 65 | cursor: pointer; 66 | display: flex; 67 | align-items: center; 68 | justify-content: center; 69 | } 70 | .task .radio .icon { 71 | color: #fff; 72 | font-size: 16px; 73 | opacity: 0; 74 | pointer-events: none; 75 | transition: all 0.3s ease; 76 | } 77 | .task.selected .radio { 78 | background-color: var(--primary-color); 79 | } 80 | .task.task.selected .radio .icon { 81 | opacity: 1; 82 | } 83 | .task .right { 84 | flex: 1; 85 | } 86 | .task .title { 87 | font-size: 16px; 88 | font-weight: 500; 89 | color: #333; 90 | } 91 | .task .description { 92 | font-size: 14px; 93 | color: #333; 94 | } 95 | .task .info { 96 | display: flex; 97 | gap: 10px; 98 | align-items: center; 99 | font-size: 12px; 100 | color: #8a8d9b; 101 | margin-top: 10px; 102 | } 103 | .task .info.expired { 104 | color: #f00; 105 | } 106 | .task .info .dot { 107 | font-size: 5px; 108 | color: #ddd; 109 | } 110 | .tasks-wrapper .delete { 111 | width: 40px; 112 | height: 40px; 113 | border-radius: 50%; 114 | position: absolute; 115 | right: 20px; 116 | bottom: 220px; 117 | color: #bfc1ce; 118 | display: none; 119 | align-items: center; 120 | justify-content: center; 121 | border: 1px solid #eee; 122 | cursor: pointer; 123 | transition: all 0.3s ease; 124 | } 125 | .tasks-wrapper .delete:hover { 126 | color: #f00; 127 | border-color: #f00; 128 | } 129 | .tasks-wrapper .delete.show { 130 | display: flex; 131 | } 132 | .tasks-wrapper .no-tasks { 133 | height: 100%; 134 | display: flex; 135 | align-items: center; 136 | justify-content: center; 137 | } 138 | .add-wrapper { 139 | width: 110%; 140 | padding: 20px; 141 | background-color: #fff; 142 | border-radius: 10px; 143 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 144 | } 145 | .add-wrapper .details { 146 | display: flex; 147 | flex-direction: column; 148 | margin-bottom: 10px; 149 | } 150 | .add-wrapper .details input, 151 | .add-wrapper .details textarea { 152 | font-size: 12px; 153 | color: #333; 154 | } 155 | 156 | .add-wrapper .details input::placeholder, 157 | .add-wrapper .details textarea::placeholder { 158 | color: #bfc1ce; 159 | } 160 | .add-wrapper .details input { 161 | font-weight: 500; 162 | margin-bottom: 10px; 163 | } 164 | .add-wrapper .details textarea { 165 | resize: none; 166 | height: 100px; 167 | } 168 | .add-wrapper .footer { 169 | display: flex; 170 | justify-content: space-between; 171 | align-items: center; 172 | } 173 | .footer .info { 174 | font-size: 12px; 175 | color: #8a8d9b; 176 | display: flex; 177 | gap: 20px; 178 | } 179 | .footer .info label { 180 | position: relative; 181 | padding: 5px 10px; 182 | border-radius: 5px; 183 | display: flex; 184 | align-items: center; 185 | gap: 10px; 186 | cursor: pointer; 187 | text-transform: capitalize; 188 | border: 1px solid #eee; 189 | } 190 | .footer .info input { 191 | position: absolute; 192 | top: 0; 193 | left: 0; 194 | opacity: 0; 195 | cursor: pointer; 196 | } 197 | .footer .info input::-webkit-calendar-picker-indicator { 198 | position: absolute; 199 | left: 0; 200 | right: 0; 201 | top: 0; 202 | bottom: 0; 203 | cursor: pointer; 204 | height: auto; 205 | width: auto; 206 | background-color: transparent; 207 | color: transparent; 208 | } 209 | .footer .btn { 210 | padding: 5px 10px; 211 | border-radius: 5px; 212 | font-size: 12px; 213 | font-weight: 500; 214 | color: #fff; 215 | text-transform: capitalize; 216 | cursor: pointer; 217 | transition: all 0.3s ease; 218 | } 219 | .footer .btn.clear { 220 | color: #8a8d9b; 221 | border: 1px solid #eee; 222 | background-color: transparent; 223 | } 224 | .footer .btn.add { 225 | background-color: var(--primary-color); 226 | } 227 | 228 | /* media queries */ 229 | @media screen and (max-width: 670px) { 230 | body { 231 | display: block; 232 | } 233 | .container { 234 | width: 100%; 235 | border-radius: 0; 236 | } 237 | .add-wrapper { 238 | width: 100%; 239 | border-radius: 0; 240 | } 241 | } 242 | 243 | @media screen and (max-width: 400px) { 244 | .tasks-wrapper .delete { 245 | bottom: 280px; 246 | } 247 | .add-wrapper .footer { 248 | flex-direction: column; 249 | gap: 10px; 250 | } 251 | } 252 | --------------------------------------------------------------------------------