멍청이 짓 2. TODOLIST 비하인드 : addEventListener깨달음

AREUM·2023년 1월 27일
0

멍청한 짓

목록 보기
2/5
post-thumbnail

현재 상황

  1. 이벤트 버블링을 이용해 ul태그에 이벤트를 click을 걸었고, checkeBoxclick했을 때, 함수가 작동 한다.
  2. li값에 newToDo라는 객체내에 key값을 가져와 liid를 주었다.
  3. 그것을 조건을 걸어 로컬스토리지에 있는 id값과 비교해서 값이 같을 때, li"checked"라는 class를 붙여주는 상황이다.

Cannot read properties of null (reading 'id’) 에러가 발생했다.

로컬스토리지에 있는 id값을 console.log에 찍었다.

리스트가 3개 있다는 가정하에 checkeBox를 클릭했을 때,
처음 id를 찍지 못하고 빈값을 가져왔고, 한번 더 함수가 돈 뒤 값을 가지고 왔다.
총 렌더링이 2번된거다.

에러 코드

const todoForm = document.querySelector(".form_group");
const todoInput = todoForm.querySelector(".todo_input");
const todoListUI = document.querySelector(".todo");
const TODOLIST_KEY = "todolist";
const CHECKED = "checked";
let spaceToDo = [];

// 로컬스토리지 안에 key값을 저장한다. ( stringify : 오브젝트를 문자형으로 변환한다. )
function saveToDo() {
  localStorage.setItem(TODOLIST_KEY, JSON.stringify(spaceToDo));
}

// 리스트 삭제
const deleteToDo = function (e) {
  const todoli = e.target.parentElement;
  console.log(todoli);

  const todoOneDelete = spaceToDo.filter((todo) => {
    return todo.id !== parseInt(todoli.id);
  });
  spaceToDo = todoOneDelete;

  todoli.remove();
  saveToDo();
  totalCount();
};

// checked가 되었을 때, checked상태 로컬스토리지에 반영.
const todoChecked = function (e) {
  const todoinput = e.target;
  const todolabel = e.target.parentElement;
  const todoli = todolabel.parentElement;
  const del = todolabel.nextSibling;

  spaceToDo.filter((todo) => {
    // li의 id와 input의 id를 비교해 로컬스토리지에 checked를 true, false 반영시켜주기.
    if (todo.id === parseInt(todoli.id)) todo.checked = !todo.checked;	// !!error!!
    console.log("todo.id", todo.id);
    console.log("todo.checked", todo.checked);
    console.log("todoli.id", todoli.id);
  });

  todoinput.checked === true
    ? todoli.classList.add(CHECKED)
    : todoli.classList.remove(CHECKED);

  saveToDo();
  del.addEventListener("click", deleteToDo);
};

// 전체적인 list생성이 화면에 보여지는 부분.
const drawingTodo = function (newToDo) {
  const todoList = document.createElement("li");
  const todoLabel = document.createElement("label");
  const todoCheckBox = document.createElement("input");
  const deleteBtn = document.createElement("button");

  const { id, text, checked } = newToDo;

  todoList.setAttribute("id", id);
  todoCheckBox.setAttribute("type", "checkbox");
  todoLabel.textContent = text;
  deleteBtn.textContent = "삭제";

  todoList.classList.add("list");
  deleteBtn.classList.add("delete_btn");

  todoListUI.prepend(todoList);
  todoList.appendChild(todoLabel);
  todoList.appendChild(deleteBtn);
  todoLabel.prepend(todoCheckBox);

  listZero();
  totalCount();
};

// list 내부를 관리하는 몸통
const todoSubmitHandler = function (e) {
  e.preventDefault();
  const InputValue = todoInput.value;
  todoInput.value = "";

  const newToDoObj = {
    id: Date.now(),
    text: InputValue,
    checked: false,
  };
  // 로컬스토리지에 배열안에 객체를 넣어준다.
  spaceToDo.push(newToDoObj);

  saveToDo();
  drawingTodo(newToDoObj);
};
todoForm.addEventListener("submit", todoSubmitHandler);
todoListUI.addEventListener("click", todoChecked);	// checkeBox클릭 시!!!!!

// 로컬스토리지 안에 있는 TODOLIST_KEY를 읽어온다. (parse : 문자열을 오브젝트로 변환시킨다.)
const getToDo = JSON.parse(localStorage.getItem(TODOLIST_KEY));

// 로컬스토리지 안에 값이 null이 아닐때 drawingTodo함수를 실행시킨다.
if (getToDo !== null) {
  spaceToDo = getToDo; // 배열안에 변수를 할당 해준다.
  getToDo.forEach(drawingTodo);
}

처음에는 배열연산하는 메소드가 잘못 된건가 생각했다.
왜 ? 왜 ? 꼬리 쫒듯 이유를 찾다가 click이 아니라 change가 되었을 때 ?
즉, 값이 변했을 때 함수가 실행되는 거면 달라지지 않을까 ? 생각했다.

clickchange로 바꿔주니 빈값을 반환하지 않았고, 잘 작동되었다.

코드 수정

const todoForm = document.querySelector(".form_group");
const todoInput = todoForm.querySelector(".todo_input");
const todoListUI = document.querySelector(".todo");
const TODOLIST_KEY = "todolist";
const CHECKED = "checked";
let spaceToDo = [];

// 로컬스토리지 안에 key값을 저장한다. ( stringify : 오브젝트를 문자형으로 변환한다. )
function saveToDo() {
  localStorage.setItem(TODOLIST_KEY, JSON.stringify(spaceToDo));
}

// 리스트 삭제
const deleteToDo = function (e) {
  const todoli = e.target.parentElement;

  const todoOneDelete = spaceToDo.filter((todo) => {
    return todo.id !== parseInt(todoli.id);
  });
  spaceToDo = todoOneDelete;

  todoli.remove();
  saveToDo();
  totalCount();
};

// checked가 되었을 때, checked상태 로컬스토리지에 반영.
const todoChecked = function (e) {
  const todoinput = e.target;
  const todoli = todoinput.parentElement.parentElement;

  spaceToDo.filter((todo) => {
    // li의 id와 input의 id를 비교해 로컬스토리지에 checked를 true, false 반영시켜주기.
    if (todo.id === parseInt(todoli.id)) todo.checked = !todo.checked;

    todoinput.checked === true
      ? todoli.classList.add(CHECKED)
      : todoli.classList.remove(CHECKED);
  });

  saveToDo();
};

// 전체적인 list생성이 화면에 보여지는 부분.
const drawingTodo = function (newToDo) {
  const todoList = document.createElement("li");
  const todoLabel = document.createElement("label");
  const todoCheckBox = document.createElement("input");
  const deleteBtn = document.createElement("button");

  const { id, text, checked } = newToDo;

  todoList.setAttribute("id", id);
  todoCheckBox.setAttribute("type", "checkbox");
  todoCheckBox.checked = checked;
  todoLabel.textContent = text;
  deleteBtn.textContent = "삭제";

  todoList.classList.add("list");
  deleteBtn.classList.add("delete_btn");

  todoListUI.prepend(todoList);
  todoList.appendChild(todoLabel);
  todoList.appendChild(deleteBtn);
  todoLabel.prepend(todoCheckBox);

  todoCheckBox.checked === true
    ? todoList.classList.add(CHECKED)
    : todoList.classList.remove(CHECKED);

  listZero();
  totalCount();
  deleteBtn.addEventListener("click", deleteToDo);	// 리스트 삭제!!!
};

// list 내부를 관리하는 몸통
const todoSubmitHandler = function (e) {
  e.preventDefault();
  const InputValue = todoInput.value;
  todoInput.value = "";

  const newToDoObj = {
    id: Date.now(),
    text: InputValue,
    checked: false,
  };
  // 로컬스토리지에 배열안에 객체를 넣어준다.
  spaceToDo.push(newToDoObj);

  saveToDo();
  drawingTodo(newToDoObj);
};
todoForm.addEventListener("submit", todoSubmitHandler);
todoListUI.addEventListener("change", todoChecked);	// !!!change로 수정!!!!

// 로컬스토리지 안에 있는 TODOLIST_KEY를 읽어온다. (parse : 문자열을 오브젝트로 변환시킨다.)
const getToDo = JSON.parse(localStorage.getItem(TODOLIST_KEY));

// 로컬스토리지 안에 값이 null이 아닐때 drawingTodo함수를 실행시킨다.
if (getToDo !== null) {
  spaceToDo = getToDo; // 배열안에 변수를 할당 해준다.
  getToDo.forEach(drawingTodo);
}

Click, change의 개념을 좀 더 명확하게 알게되었다.

click, change의 차이점

click
: 해당영역이 click했을 때.

change
: 해당영역의 값이 변했을 때.

이 후 추가수정

삭제 버튼이 이벤트가 change되었을 때, 실행 되는 함수안에 들어가 있어서
문제이슈 : checkeBoxchecked되게 하고 새로고침을 하고 삭제버튼을 클릭 했을 때, 두번클릭 해야 삭제가 되는 현상이 발생하게 되었다.

원인은 로컬스토리지에 작동되는 함수(todoChecked)안에 있었기에,
drawingTodo 함수안에 삭제버튼 이벤트를 넣어 수정했다.

다음 순서로 넘어 가야되겠다.
여기까지 오는데 참 오래걸렸다.

지금 심정

완성해서 얼른 최종을 업로드 해야되겠다.
TMI 지만, 사실 졸라 해매다가 addEventListener관련 공부하고 벨로그에 올리고 난 뒤, 코드를 싹은 아니지만 뒤집어 엎고, 마크업부터 css까지 다 그냥 제대로 만들었다.

사실 너무 열받아서 뒤집어 엎었다.
처음에 html 대충 form태그 만들어서 때려박아놓고 그냥 시작한거고, 처음부터 잘못된거 같은 느낌이라 그냥 마크업부터 다시 했다.

profile
어깨빵으로 부딪혀보는 개발끄적이는 양씨 인간

0개의 댓글