What To Do If Your Brain Always Goes todotodo ♪♪

https://ngtrangtee.github.io/JavaScript%20DOM/jsdom-bai6/buoi-6/index-bai6.html

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