
IMG Files
HTML Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Todo</title>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,600;0,700;0,800;1,400&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="./css/style.css" />
</head>
<body>
<div class="container">
<h1>Todo List</h1>
<!-- Box Nhập công việc -->
<div class="todo-input">
<input
type="text"
placeholder="Nhập công việc"
id="todo-input"
autocomplete="off"
/>
<button id="btn-add">Thêm</button>
<button id="btn-update">Edit</button>
</div>
<!-- Box chọn option (all - Hoàn thành - Chưa hoàn thành) -->
<div class="todo-option">
<div class="todo-option-item">
<input
type="radio"
name="todo-option-item"
id="all"
checked
value="1"
/>
<label for="all">All</label>
</div>
<div class="todo-option-item">
<input type="radio" name="todo-option-item" id="unactive" value="2" />
<label for="unactive">Chưa hoàn thành</label>
</div>
<div class="todo-option-item">
<input type="radio" name="todo-option-item" id="active" value="3" />
<label for="active">Hoàn thành</label>
</div>
</div>
<!-- Danh sách todo -->
<div class="todo-container">
<div class="todo-list">
<div class="todo-item active-todo">
<div class="todo-item-title">
<input type="checkbox" checked />
<p>Làm bài tập Javascript Phần array + string</p>
</div>
<div class="option">
<button class="btn btn-update">
<img src="./img/pencil.svg" alt="icon" />
</button>
<button class="btn btn-delete">
<img src="./img/remove.svg" alt="icon" />
</button>
</div>
</div>
<div class="todo-item">
<div class="todo-item-title">
<input type="checkbox" />
<p>Mua đồ ăn trưa</p>
</div>
<div class="option">
<button class="btn btn-update">
<img src="./img/pencil.svg" alt="icon" />
</button>
<button class="btn btn-delete">
<img src="./img/remove.svg" alt="icon" />
</button>
</div>
</div>
<div class="todo-item active-todo">
<div class="todo-item-title">
<input type="checkbox" checked />
<p>Đá bóng</p>
</div>
<div class="option">
<button class="btn btn-update">
<img src="./img/pencil.svg" alt="icon" />
</button>
<button class="btn btn-delete">
<img src="./img/remove.svg" alt="icon" />
</button>
</div>
</div>
</div>
</div>
<div class="delete-all">
<button id="btn-delete-all">Delete All</button>
</div>
</div>
<script src="./js/main.js"></script>
</body>
</html>
CSS Code
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
font-family: "Open Sans", sans-serif;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: #f4eee8;
}
img {
display: inline-block;
max-width: 100%;
object-fit: cover;
}
.container {
background-color: #325288;
padding: 50px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
h1 {
text-transform: capitalize;
text-align: center;
margin-bottom: 30px;
font-weight: normal;
font-size: 2.6rem;
letter-spacing: 0.05em;
color: #fff;
}
.todo-input {
height: 3rem;
padding: 0 50px;
}
.todo-input input {
min-width: 350px;
height: 100%;
line-height: 40px;
border: none;
padding-left: 6px;
padding-right: 6px;
outline: none;
}
.todo-input input:focus {
outline: none;
}
.todo-input button,
.delete-all button {
text-transform: uppercase;
display: inline-block;
height: 100%;
padding: 0 1.3rem;
border: 1px solid rgba(255, 255, 255, 0.3);
background-color: #114e60;
color: #fff;
cursor: pointer;
transition: all 0.3s ease;
outline: none;
}
.todo-input button:hover,
.delete-all button:hover {
opacity: 0.6;
}
#btn-update {
display: none;
}
.todo-option {
display: flex;
justify-content: flex-end;
padding: 1rem 50px 0;
}
.todo-option-item {
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 6px;
margin-left: 6px;
font-size: 14px;
display: flex;
align-items: center;
}
.todo-option-item input {
margin-right: 4px;
}
.todo-container {
margin-top: 20px;
}
.todo-item {
display: flex;
justify-content: space-between;
border-bottom: 1px solid #f4f4f4;
margin-bottom: 6px;
padding: 15px 50px;
font-size: 16px;
background-color: rgba(255, 255, 255, 0.1);
}
.todo-item.active-todo .todo-item-title p {
position: relative;
font-style: italic;
}
.todo-item.active-todo .todo-item-title p::after {
content: "";
position: absolute;
left: 0;
top: 50%;
height: 2px;
width: 100%;
background-color: #fff;
}
.todo-item-title {
display: flex;
align-items: center;
}
.todo-item-title input {
margin-right: 6px;
}
.btn {
opacity: 0;
border: none;
outline: none;
background-color: transparent;
cursor: pointer;
font-size: 20px;
transition: all 0.2s ease-in;
}
.todo-item:hover .btn {
opacity: 1;
}
.btn img {
display: inline-block;
width: 20px;
height: 20px;
}
.todos-empty {
padding-left: 50px;
font-style: italic;
}
JS Code
let todos = [
// {
// id: randomId(),
// title: "Coding homework",
// status: false,
// },
// {
// id: randomId(),
// title: "Edit Podcast",
// status: false,
// },
// {
// id: randomId(),
// title: "Business Analytics Individual Quiz",
// status: true,
// },
];
const todoListEl = document.querySelector(".todo-list");
const todoInputEl = document.querySelector("#todo-input");
const btnAdd = document.querySelector("#btn-add");
const btnUpdate = document.querySelector("#btn-update")
const btnDelete = document.querySelector("#btn-delete-all")
const inputs = document.querySelectorAll(".todo-option-item input");
function getValueOption() {
for (i = 0; inputs.length; i++) {
if (inputs[i].checked == true) {
return Number(inputs[i].value); //convert ra số
}
}
}
// Hiển thị lên trên giao diện
function renderToDo(arr) {
// Làm rỗng array
let newArr;
let valueOption = getValueOption();
switch (valueOption) {
case 1: //lấy tất cả cv
newArr = [...arr]; //... là clone ra một mảng mới, khoogn ảnh huo
break;
case 2: //Lấy cx hoàn thanh
newArr = arr.filter((todo) => todo.status == false);
break;
case 3: //Lấy cv chưa hoàn thành
newArr = arr.filter((todo) => todo.status == true);
break;
default:
newArr = [...arr];
break;
}
todoListEl.innerHTML = "";
// Array rỗng -> Hiển thị không có công việc trong danh sách
// Có data -> Render bình thường
if (newArr.length == 0) {
todoListEl.innerHTML = `<p class="todos-empty">Không có công việc trong danh sách</p>`;
return; //để kết thúc
}
for (let i = 0; i < newArr.length; i++) {
const t = newArr[i];
todoListEl.innerHTML += `
<div class="todo-item ${t.status ? "active-todo" : ""}">
<div class="todo-item-title">
<input type="checkbox"
${t.status ? "checked" : ""}
onclick="toggleStatus(${t.id})"
>
<p>${t.title}</p>
</div>
<div class="option">
<button class="btn btn-update"
onclick="updateTodo(${t.id})">
<img src="./img/pencil.svg" alt="icon">
</button>
<button class="btn btn-delete"
onclick="deleteTodo(${t.id})"
>
<img src="./img/remove.svg" alt="icon">
</button>
</div>
</div>`;
}
// Lọc - Lấy ra value trong input filter - duyệt qua 3 ô input, lấy ra ô nào có trạng thái checked, lấy ra value -1,2,3
// Lọc - Dựa vào value để lọc ra các công việc tương ứng
// Lọc - Hiển thị công việc sau khi lọc
//Tạo ra mảng rỗng để ứng kết quả sau khi lọc
}
renderToDo(todos);
// Không truy cập vào dom được nên không dùng eventListener được
// Random ID
function randomId() {
return Math.floor(Math.random() * 99999 + 1);
}
console.log(todos);
// thay đổi trạng thái
// Duyệt mảng todos, tìm công việc có id = id truyền vào
// Nếu status = true > Chuyển thành false
// Nếu status = false > chuyển thành true
// gọi renderTodo() để hiện thị lại trên giao diện
function toggleStatus(id) {
for (let i = 0; i < todos.length; i++) {
if (id == todos[i].id) {
if (todos[i].status == false) {
todos[i].status = true;
} else {
todos[i].status = false;
}
// Hoặc: todos[i].status = !todos[i].status
}
}
setDataToLocalStorage(todos);
renderToDo(todos);
}
// todos[i] là object
// todos là mảng
// id là số
// i là vị trí
// Xóa phần tử trong todos
// Duyệt mảng todos, tìm công việc có id trùng với id truyền vào trong func
// Xóa đi - dùng splice
// Sau khi xóa, gọi lại renderTodo() để hiển thị lại
function deleteTodo(id) {
for (let i = 0; i < todos.length; i++) {
if (id == todos[i].id) {
todos.splice(i, 1);
}
}
setDataToLocalStorage(todos);
renderToDo(todos);
}
// Thêm công việc
// 1. Lấy dữ liệu từ ô input, gắn vào 1 biến "title" /
// 2. Kiểm tra dữ liệu có trống hay không, nếu có thì thông báo => return
// 3. Tạo ra cấu trúc của new todos
// id: random ngẫu nhiên (gọi function createId())
// title: Nội dung trong ô
// status: false
// 4. Push new todo vào todos
// 5. Gọi renderTodo để hiển thị lại
btnAdd.addEventListener("click", function () {
title = todoInputEl.value;
if (title == "") {
alert("Cannot leave task blank");
return;
} else {
let newTodos = [
{
id: randomId(),
title: title,
status: false,
},
];
todos.push(...newTodos);
}
// or: let newTodo = {
// id: randomId(),
// title: title,
// status: false,
// }
setDataToLocalStorage(todos);
renderToDo(todos);
todoInputEl.value = ""; //clear nội dung trong ô input
});
// Lọc công việc
// truy cập vào ô input
// Dùng vòng lặp để lắng nghe sự kiện, tại 1 thời điểm chỉ có 1 cái được click
// để itmf ra ô input có thuộc tính check
// Lấy ra value là 1 khi click vào all
// Có thể sử dụng if else hoặc switch case để lọc
// Khi họ check thì lại render một lần nữa
for (let i = 0; i < inputs.length; i++) {
inputs[i].addEventListener("change", function () {
renderToDo(todos);
});
}
// Sửa
// Truy cập vào nút sửa, ô input, nút thêm
// Lấy title
// Chuyển title lên ô input
// Ẩn nút thêm, thêm nút sửa
// Đổi nút thêm thành nút sửa
// chuyển nội dung input vào lại vị trí đó
function updateTodo(id) {
for (let i = 0; i < todos.length; i++) {
if (id == todos[i].id) {
idUpdate = todos[i].id
todoInputEl.value = todos[i].title
btnAdd.style.display = "none"
btnUpdate.style.display = "inline-block"
}
}
}
let idUpdate = 0
btnUpdate.addEventListener("click", function () {
let content = todoInputEl.value
console.log(idUpdate)
for (let i = 0; i < todos.length; i++) {
if (idUpdate == todos[i].id) {
todos[i].title = content
}
}
setDataToLocalStorage(todos)
renderToDo(todos)
})
// + Lấy title, chuyển lên ô input, đổi nút thêm thành nút sửa
// +tạo ra một cái modal giữa màn hình và sửa ở đấy (xem trên bootstrap)
// lưu dữ liệu vào local storage - giống ranking
// Lấy ra data
function getDataFromLocalStorage() {
let dataLocal = localStorage.getItem("todos");
// Nếu có dữ liệu thì gán vào
if (JSON.parse(dataLocal)) {
todos = JSON.parse(dataLocal)
} else {
todos = []
}
// Gọi renderRanking truyền vào mảng ranking để hiển thị
renderToDo(todos)
}
// Lưu data vào
function setDataToLocalStorage(arr) {
// Chuyển sang dạng JSON rồi lưu vào
localStorage.setItem('todos', JSON.stringify(arr))
}
getDataFromLocalStorage();
// Gọi function init ra
// onload là khi trang web load xong html, css (đã xuất hiện dom), thì init mới xuất hiện
// Xóa hết
btnDelete.addEventListener("click", function () {
todos = []
setDataToLocalStorage(todos);
renderToDo(todos)
})
// Sắp xếp (theo trạng thái, ngày tạo, mức ưu tiên, deadline) - phải thêm một số thuộc tính vào trực tiếp nút input (ngày tạo, mức độ ưu tiên)
I might things to do but I’m always open for a meaningful conversation.
Trang (Tee) Thao Nguyen