ToDoList Code Review

Seob·2020년 6월 30일
1
post-thumbnail

투두리스트에서 내가 만들고 싶었던 기능은 다음과 같다.

  • 리스트에 아이템을 추가 할 수 있을 것
  • 아이템을 삭제할 수 있을 것
  • 아이템을 완료하여 체크하여 표시할 수 있을 것
  • 위 모든 사항이 페이지 새로고침을 해도 남아있을 것

HTML

        <div class="todoInputContainer">
          <form class="js-todoForm todoForm">
            <input
              class="js-todoInput"
              type="text"
              placeholder="What are you going to do?"
              size="24"
            />
          </form>
          <button class="js-addTodoBtn addTodoBtn" type="submit">
            <i class="fas fa-plus-circle"></i>
          </button>
        </div>
        <ul class="js-todoList todoList"></ul>
      </div>
  • 한 틀 안에 배치하기 위해 todoInputContainer<form>, <button>, <ul>을 넣었다.
  • 클래스 이름을 두 개로 나눈 이유는 JS에서 사용할 이름과 CSS에서 사용할 이름을 구분하기 위함이다.

JavaScript

const todoForm = document.querySelector(".js-todoForm");
const todoInput = todoForm.querySelector(".js-todoInput");
const addTodoBtn = document.querySelector(".js-addTodoBtn");
const todoList = document.querySelector(".js-todoList");
const TODOS_LS = "todos";
let todos = [];
  • HTML의 Class를 변수로 지정해준다.
  • TODOS_LS의 LS는 Local Storage 이고 자세한건 아래에서 설명하겠다.
  • let todos = []; const 말고 let을 사용한 이유는 나중에 바뀌어야 하기 때문이다. 투두 아이템이 저장될 부분.

리스트에 아이템 추가하기 ➕

투두리스트에서 가장 먼저 할 일은? 리스트에 할 일을 추가하는 것이다.
추가를 하기 위해선 input에 추가할 내용을 적을 것이고, 엔터를 누르거나 추가 버튼을 클릭해서 그 내용을 submit해야 한다.

이 과정을 처리하는 함수를 살펴보자.

function todoHandleSubmit(event) {
  //prevent the form from submitting
  event.preventDefault();
  const currentValue = todoInput.value;
  printTodo(currentValue);
  //empty the input
  todoInput.value = "";
}
  • input에 아무거나 넣어보면 화면이 새로고침이 되는 것처럼 된다. 이를 방지하기 위해 preventDefault()를 추가한다.
  • const currentValue = todoInput.value; - input에 적혀있는 값을 변수로 선언한다.
  • printTodo함수에 input에 적혀있는 값을 포함하여 호출한다.
  • todoInput.value = ""; 호출 후 input을 비워준다.

input에서 입력받은 내용은 어떻게 표시될지 아래에서 다뤄보자.

function printTodo(text) {
  //create li
  const todoLi = document.createElement("li");
  todoLi.classList.add("todoLiClass");
  //create complete button
  const completeBtn = document.createElement("button");
  completeBtn.innerHTML = '<i class="fas fa-check"></i>';
  completeBtn.classList.add("completeBtnClass");
  completeBtn.addEventListener("click", completeBtnFunction);
  //create span
  const newTodo = document.createElement("span");
  newTodo.innerText = text;
  newTodo.classList.add("newTodoClass");
  //create delete button
  const deleteBtn = document.createElement("button");
  deleteBtn.innerHTML = '<i class ="fas fa-trash"></i>';
  deleteBtn.classList.add("deleteBtnClass");
  //event listener
  deleteBtn.addEventListener("click", deleteBtnFunction);
  //create new id for todoLi and todoObj so they can be handled idividually
  const newId = todos.length + 1;
  todoLi.id = newId;
  // append created things in the list item
  todoLi.appendChild(completeBtn);
  todoLi.appendChild(newTodo);
  todoLi.appendChild(deleteBtn);
  //append the list item in the unordered list
  todoList.appendChild(todoLi);
  //create todo object to push in the todos array
  const todoObj = {
    text: text,
    id: newId,
  };
  //push the object in the todos array
  todos.push(todoObj);
  //save the array in the local storage
  saveTodos(todos);
}
  • html에 있는 <ul>에 리스트 아이템을 추가해야 하므로 createElement("li")로 리스트 아이템을 만들어줬고, 나중에 CSS에서 수정하기 위해 classList.add("todoLiClass")로 클래스 이름을 지정해주었다.
  • 삭제와 완료 기능을 하는 버튼도 추가해준다.
  • <span>은 리스트 아이템의 텍스트 부분이 표시될 부분이다.
  • newTodo.innerText = text;text는 함수를 호출하며 입력되는 값을 받아온다.
  • const newId = todos.length + 1; 나중에 완료하고 삭제할 때 특정 id의 아이템만 다뤄져야 하므로 추가한 부분이고 +1을 한 이유는 todosarray이고 비어 있는 상태에서 array의 길이는 0이기 때문이다.
  • appendChild는 위에서 만든 <button>,<span><li>에 넣어주는 과정이다.
  • 그리고 이렇게 만들어진 <li><ul>에 넣어준다.
  • todos.push(todoObj);를 통해 배열에 오브젝트를 추가한다.
  • saveTodos(todos); 오브젝트가 추가된 배열을 Local Storage에 저장하여 새로고침을 하여도 데이터가 남아있도록 한다.

다음으로 saveTodos 함수를 살펴보자.

추가된 아이템 저장하기 💾

function saveTodos() {
  localStorage.setItem(TODOS_LS, JSON.stringify(todos));
}
  • setItem을 이용하여 TODOS_LS에 string형태로 넣어준다.
  • Local Storage에는 자바스크립트의 데이터는 저장될 수 없고 string만 저장할 수 있기 때문이다.

하지만 이렇게 저장했어도 새로고침을 하면 리스트에는 아이템이 없는 것처럼 보인다. 이것은 저장된 데이터를 불러오지 않았기 때문이다.

다음으로 저장된 데이터를 불러오는 함수를 살펴보자.

저장된 데이터 불러오기 📲

투두리스트가 가장 처음에 해야 할 일은 이미 추가된 아이템이 있는지 확인하고 불러와야 할 것이다.
그렇기 때문에 다음과 같은 함수로부터 출발할 수 있다.

function init() {
  loadTodos();
  todoForm.addEventListener("submit", todoHandleSubmit);
}
init();
  • EventTargetaddEventListner메소드는 지정한 이벤트가 대상에 전달될 때마다 호출할 함수를 설정한다고 한다. 👉🏻DMN
  • 즉, init()함수는 loadTodos() 함수를 호출 후, todoForm에게 submit이벤트가 전달될 때 todoHandleSubmit함수가 호출된다는 의미이다.

이어서 loadTodos 함수를 살펴보자.

function loadTodos() {
  const loadedTodos = localStorage.getItem(TODOS_LS);
  if (loadedTodos !== null) {
    //string(loadedTodos) to object
    const parsedTodos = JSON.parse(loadedTodos);
    //run functions each time
    parsedTodos.forEach((itIsMeaningless) => {
      //write the extracted texts in todoInput
      printTodo(itIsMeaningless.text);
    });
  }
}
  • 로컬스토리지에 저장이 된 데이터를 변수로 지정해준 것이 loadedTodos이다.
  • if조건문을 통해 loadedTodos가 비어있지 않다면, input에 아이템을 입력하여 프린트하는 내용이다.
  • 로컬 스토리지에 저장할 때 string형태로 저장을 했으므로 JSON.parse를 이용해 오브젝트로 다시 되돌려준다.
  • forEach는 각 배열 요소에 각각에 대해 실행하는 메소드이다. 👉🏻DMN
  • itIsMeaningless부분은 아무거나 적어도 관계가 없어서..그냥...😏
  • parsedTodos.forEach(function(anyName) { printTodo(anyName.text);}); 이런 식으로 적어줄 수도 있다.
  • printTodo(itIsMeaningless.text); 각 배열 요소의 text 부분을 printTodo로 보내준다.
  • 이제 새로고침을 해도 리스트 아이템이 사라지지 않고 그대로 남아있게 된다.

버튼에 기능 추가하기 🔘

printTodo함수에서 각 버튼에 다음과 같은 이벤트 리스너를 추가해 두었다.

completeBtn.addEventListener("click", completeBtnFunction);
deleteBtn.addEventListener("click", deleteBtnFunction);

이는 click이벤트가 발생했을 때 해당 함수를 호출하게 해준다.

deleteBtnFunction부터 보도록 하자

삭제 버튼 ❌

function deleteBtnFunction(event) {
  const todoItem = event.target;
  const li = todoItem.parentNode;
  li.classList.add("todoFall");
  //    wiat for the transition to end
  li.addEventListener("transitionend", function () {
    // todoList.removeChild(li); //this doesn't work properly with transitionend..
    li.remove();
  });
  //clean todos from the local storage
  const cleanTodos = todos.filter(function (findIt) {
    //since li.id is not a number but a sting
    return findIt.id !== parseInt(li.id);
  });
  todos = cleanTodos;
  saveTodos();
}
  • console.dir(event.tartget)으로 해당 버튼의 부모노드를 찾아내서 버튼이 해당된 부모 리스트를 지워주면 버튼만 삭제되지 않고 리스트 자체가 삭제된다.
  • const li = todoItem.parentNode; 이렇게 리스트 아이템이 들어있던 부모 리스트를 변수로 지정해준다.
  • transitionend를 사용한 이유 : 삭제될 때 transition으로 사라지는 효과를 CSS에서 주었는데 삭제와 동시에 이루어지기 때문에 애니메이션 효과를 볼 수 없었다. 그래서 transition이 끝나고 함수를 호출하게 해주는 메소드인 transitionend를 사용하였다.
  • 그리고 왜인지는 모르겠지만 transitioned를 사용하지 않고 removeChild(li)를 사용하면 정상적으로 삭제가 잘 됐는데 transitionend와 함께 잘 작동하지 않는 것 같아서 li.remove()를 사용했다.
  • 겉으로 보여지는 리스트 아이템을 삭제했으니 로컬 스토리지에서도 삭제를 해주어야 하므로 filter 메소드를 사용했다.
  • return findIt.id !== parseInt(li.id); - Local Storagestring만을 저장하므로 숫자로 바꿔줄 필요가 있었다. parseInt는 문자열을 정수로 바꿔준다.
  • todos = cleanTodos;로 삭제된 배열로 새로 업데이트해 주고 saveTodos로 업데이트 된 데이터를 저장한다.

완료 버튼 ✅

function completeBtnFunction(event) {
  const todoItem = event.target.parentNode;
  if (todoItem.classList[0] === "todoLiClass") {
    todoItem.classList.toggle("todoCompleted");
  }
}
  • 삭제 버튼과 마찬가지로 부모노드를 찾아서 변수로 정해주고 클래스 이름이 원하는 이름과 일치하면 todoCompleted를 토글시키는 함수이다.
  • 완료 상태를 저장하기 위해서는 너무 복잡하고 시간을 들여도 잘 되지 않았기에 깔끔하게 포기했다 🤓ㅎ,,

todoCompleted in CSS

.todoCompleted {
  font-style: italic;
  color: lightgray;
  opacity: .2;
  text-decoration: line-through;
}

위의 CSS 효과를 클릭 시마다 토글시켜준다.

추가 버튼 ➕

추가 버튼도 위의 버튼들과 마찬가지로 이벤트 리스너를 추가해주고 함수를 만들어주면 된다.

addTodoBtn.addEventListener("click", addTodoBtnFunction);
function addTodoBtnFunction() {
  printTodo(
    event.target.parentNode.parentNode.childNodes[1].childNodes[1].value
  );
  todoInput.value = "";
}
  • console.dir(event.target); 으로 input의 경로를 찾은 후 .value메소드로 입력창에 입력되어 있는 값을 printTodo함수로 넘겨준다.
  • todoInput.value = ""; - todoHandleSumit에서와 마찬가지로 입력창을 비워준다.

투두 리스트는 이것으로 기능 구현이 끝이다.🙆🏻‍♂️

이름을 넣는 부분과 시계는 그냥 보너스로 넣어본 것이니 따로 리뷰는 하지 않겠다. (귀찮은 거 아님ㅎ)

아쉬운 부분 ☹️

  • 완료 상태를 저장시키지 못한 부분은 좀 아쉽다. (투두리스트에서 진행 상황이 저장이 안 된다니..?!)
  • 할 것/한 것/지운 것 등 분류를 할 수 있게 해도 좋을 것 같다.
  • transitionend를 사용했음에도 불구하고 가끔 효과가 씹히는(?) 경우가 생긴다. 🤷🏻‍♂️ 왜 때문이지..?

위코드의 사전 스터디인 2주 차 목표였던 투두리스트를 이렇게 끝내고 나니 자바스크립트도 재미있는 것 같다. 3주 차 과제인 파이썬도 재미있게 할 수 있었으면 좋겠다!

마지막으로 투두리스트짤 다시 보기 ㅋ,ㅋ,,

profile
Hello, world!

0개의 댓글