-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
142 lines (113 loc) · 5.39 KB
/
script.js
File metadata and controls
142 lines (113 loc) · 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
let toDos = []; //store todos in memory
let currentFilter = "all"; //default filter
function loadTodos() {
try{ //load the todos, if fails, set to blank
const stored = localStorage.getItem("toDos");
toDos = stored ? JSON.parse(stored) : []; //parse json into an array, fall back to [] on an error
} catch {
toDos = [];
}
}
function saveTodos() {
localStorage.setItem("toDos", JSON.stringify(toDos)); //save todos to storage and convert array back to json
}
function getText() {
return document.getElementById("todo-input").value; //get the value typed in the form input
}
function renderTodo(todo) { //render one todo into the list
const list = document.getElementById("todo-list");
const item = document.createElement("li");
item.dataset.id = String(todo.id);
if (todo.done) {item.classList.add("done")}
const delbut = document.createElement("button"); //create a delete button for each list item
delbut.classList.add("delete");
delbut.textContent = "x";
delbut.setAttribute("aria-label", "Delete task");
const donebut = document.createElement("button") //create a finished button for each list item
donebut.classList.add("finished");
donebut.textContent = todo.done ? "Unfinished" : "Finished";
const textSpan = document.createElement("span"); //create a span with a class to append elements without manual spaces
textSpan.className = "todo-text";
textSpan.textContent = todo.text;
item.append(donebut, textSpan, delbut);
list.append(item); //append items to the unordered list (ul)
}
function renderAll() { //render the full list
const list = document.getElementById("todo-list");
list.innerHTML = ""; //clear existing items from ui
let visibleTodos; //decide which todos to show based on the filter
if (currentFilter === "all") {
visibleTodos = toDos;
} else if (currentFilter === "active") {
visibleTodos = toDos.filter(t => !t.done);
} else if (currentFilter === "completed") {
visibleTodos = toDos.filter(t => t.done);
}
visibleTodos.forEach(renderTodo); //render each visible todo
updateClearButton(); //update status of clear button
const activeCount = toDos.filter(t => !t.done).length; //update the items left counter
document.getElementById("item-count").textContent = `${activeCount} left`;
const emptyState = document.getElementById("empty-state"); //show empty state when there are no todos
if (toDos.length === 0) {
emptyState.style.display = "flex";
list.style.display = "none";
} else {
emptyState.style.display = "none";
list.style.display = "block";
}
}
function addText(){ //add new todo from user input
const text = getText().trim(); //get text and remove whitespace
if (!text) return;
const id = Date.now() + Math.floor(Math.random() * 1000); //generate unique id with random suffix
const todo = { id, text, done: false }; //make object
toDos.push(todo);
saveTodos();
renderAll() ;
}
function updateClearButton() { //enable or disable the clear completed button
const button = document.getElementById("clear-completed");
const hasCompleted = toDos.some(t => t.done); //check if any todo is done
button.disabled = !hasCompleted; //disable button if there is no completed todos
}
document.addEventListener("DOMContentLoaded", () => { //load saved data and render once after the DOM exists
loadTodos();
renderAll();
document.querySelector('.filters button[data-filter="all"]').classList.add('active-filter');
})
document.querySelector("form").addEventListener("submit", (doc) => {doc.preventDefault(); //listen for when the add or submit button is pressed
addText()
document.getElementById("todo-input").value = ""; //once clicked, add the text to list and set input back to blank
})
document.getElementById("todo-list").addEventListener("click", (event) => { //listen for when the delete or finished button is pressed
if (event.target.tagName !== "BUTTON") return;
const li = event.target.parentElement;
const id = li.dataset.id;
if (event.target.classList.contains("delete")) { //update array, save, then re-render
toDos = toDos.filter(t => String(t.id) !== id);
saveTodos();
renderAll();
}
else if (event.target.classList.contains("finished")) { //toggle done class on li, mirror button label, update array, then save
const isDone = li.classList.toggle("done");
event.target.textContent = isDone ? "Unfinished" : "Finished"
const idx = toDos.findIndex(t => String(t.id) === id);
if (idx !== -1) {
toDos[idx].done = isDone;
saveTodos();
renderAll();
}}})
document.querySelector(".filters").addEventListener("click", (event) => { //set current filter, re-render, and highlight the active filter button
if (event.target.tagName !== "BUTTON") return;
currentFilter = event.target.dataset.filter;
renderAll();
document.querySelectorAll(".filters button").forEach(btn =>
btn.classList.remove("active-filter")
);
event.target.classList.add("active-filter");
});
document.getElementById("clear-completed").addEventListener("click", () => { //keep only not completed todos, then save and re-render
toDos = toDos.filter(t => !t.done);
saveTodos();
renderAll();
})