list 내용 수정

YEONGHUN KO·2021년 11월 15일
0

JAVASCRIPT - TO DO LIST

목록 보기
10/15
post-thumbnail

list 내용 수정

이번에 소개할 기능은 list의 내용을 수정하는 기능이다. challenge 도중 다른 사람의 작품을 보다가 수정할 수 있는 기능을 추가한 사람이 있어서 , 그분의 코드를 읽고 이해한 다음 내 나름대로 적용해보았다. 논리의 흐름은 이렇다..

1 to-do list 더블클릭

2 내용이 없어지고 input 박스가 생겨남

3 input 박스에 내용을 입력하고 enter를 누름

4 input.value의 내용과 버튼들이 다시 생기고 localstorage(이하 LS)에 해당 list의 내용이 input.value로 수정됨

5 LS 저장

6 ESC를 누르거나 커서를 input 바깥에서 누르면 기존의 내용이 그대로 복원됨..

그럼 번호순으로 자세히 설명해보겠다..

1. 더블 클릭 input 박스가 생겨남

더블클릭은 eventlistner "dbclick" 으로 구현했고, input박스는 replacechild로 구현했다. 좀더 자세하게 코드로 알아보자.

const handleEdit = (event) => {
  const isSpan = event.target.closest("span");
  // event.target은 li 안에 있는 태그들을 말한다.closest("span")이라고 하면 그 태그들 중에 span 을 찾거나 가장 가까운걸 찾아서 return 하라는 뜻. 결국 span값만 선택하겠다는 거다.
  if (isSpan) {
    // 그게 span이라면 아래의 코드를 진행(따라서 span말고 다르걸 클릭하게 되면 이 코드가 실행안되는 거임)
    const li = isSpan.parentNode;
    const span = li.querySelector("span");
    const input = document.createElement("input");
    input.value = span.innerText; // input이 생겨나고 기존의 span 내용이 input에 입력된다
    li.querySelector(".delBtn").classList.add("non-showing");
    if (li.querySelector(".finishBtn") !== null) {
      li.querySelector(".finishBtn").classList.add("non-showing");
    }
    if (li.querySelector(".backBtn") !== null) {
      li.querySelector(".backBtn").classList.add("non-showing");
    } // 버튼들을 없애는 작업이다.
    input.name = span.innerHTML;
    input.maxLength = "100";
    input.placeholder = "값을 수정해라카이";
    input.autocomplete = "off"; // input을 클릭했을때 자동완성되는 기능이다.
    li.replaceChild(input, span); // 그렇게 만든 input이 기존 span과 교체된다
    input.focus(); // 그리고 커서가 input에 올라간다.
    input.addEventListener("keyup", handleKeyUp); // keyup 이벤트는 input에 어떤 키를 입력했을때를 가정해서 함수를 적용하는 것이다.  아래 설명으로 넘어가자.
    input.addEventListener("focusout", handleFocusOut);
  }
};

function init() {
  todoForm.addEventListener("submit", handleFormSubmit);
  entireTodolist.addEventListener("dblclick", handleEdit); // 리스트 전체를 범위로 잡고 더블클릭했을때 이벤트가 발생하게 함.
  loadState();
  restoreState();
}

2. input 박스에 내용을 입력하고 enter를 누름

input에 뭔가 키를 입력했을때 작동되는 함수이다.

const pendingList = document.getElementById("js_pending"),
  finishedList = document.getElementById("js_finished");

function updateFinishedTask(id, text) {
  finishedTask.map((finishedElement) => {
    if (finishedElement.id === id) {
      finishedElement.text = text;
    }
  });
}

function updatePendingTask(id, text) {
  pendingTask.map((pendingElement) => {
    if (pendingElement.id === id) {
      pendingElement.text = text;
    }
  });
}

function isLiInTodos(li) {
  return li.parentNode.matches("#js_pending");
}

function nonShowingRemover(li, btnName) {
  li.querySelector(`.${btnName}`).classList.remove("non-showing");
}

function buttonRestorer(li) {
  nonShowingRemover(li, "delBtn");
  if (li.querySelector(".finishBtn")) {
    nonShowingRemover(li, "finishBtn");
  } // 보니깐 nonshowingremover도 반복되어서 위에 따로 빼놓았다.
  if (li.querySelector(".backBtn")) {
    nonShowingRemover(li, "backBtn");
  }
}

3. input.value의 내용이 span의 내용이 됨. 버튼 다시 생기고 LS에 저장

function handleKeyUp(event) {
  if (event.target.value !== "" && event.key === "Enter") {
    // 무언가 입력했으면서 enter 키를 누른상태라면 아래의 코드가 실행된다.
    event.target.lastKey = event.key;
    // 둘다 로그해보면 target.key는 "Enter" 라고 나오는데 lastKey는 undefined라고 나온다. 그럼 undefined라고 되어있는게 문제가 된다는 말인데, lastKey가 도데체 무엇을 의미하는것일까. 근데 또, event를 로그하고 target.lastKey 를 따라 가보면 "Enter" 라고 되어있다. 여기는 왜 Enter라고 되어있으며 로그했을땐 왜 undefined라고 되어있었던 것일까?
    // 이게 무슨 역할을 하는지 궁금해서 삭제하고 돌려보니 아래와 같은 경고가 나온다.

    // Failed to execute 'replaceChild' on 'Node': The node to be removed is no longer a child of this node. Perhaps it was moved in a 'blur' event handler?

    // 교체하려고 하는 node가 더이상 li의 child가 아니라는 말을 한다. 뭐지... 구글링해보니 설명이 복잡해서 더 헷갈린다.. 일단 더 알아봐야겠다.

    const li = event.target.parentNode;
    const span = document.createElement("span");
    span.innerHTML = event.target.value;
    li.replaceChild(span, event.target); // input을 span으로 교체함 다시.
    if (isLiInTodos(li)) {
      // 위에 있는 isLiInTodos 함수를 살펴보자. li의 부모가 #js_pending 이랑 일치하느냐 이다. 그렇다면 아래 함수가, 아니면 그 아래함수가 실행됨.
      updatePendingTask(li.id, span.innerHTML);
    } else {
      updateFinishedTask(li.id, span.innerHTML);
    } //update함수를 살펴보면 ul 안에 있는 li 중에 지금 수정하려는 li.id 랑 같으면 그 안에 있는 텍스트를 input.value로 교체하라는 뜻이다.
    saveState(); // 그리고 LS를 저장한다.
    buttonRestorer(li);
    //  그리고 버튼을 살려냄. 이 코드는 반복이 되어서 따로 refactoring을 하였다.
  } else if (event.key === "Escape") {
    // 그게 아니고 esc를 누르면 아래코드가 실행된다.
    // 원래상태 그대로 복원시키는 것이다.
    event.target.lastKey = event.key;
    const li = event.target.parentNode;
    const span = document.createElement("span");
    span.innerHTML = event.target.name;
    li.replaceChild(span, event.target);
    buttonRestorer(li);
  }
}

4.esc, 커서 아웃

그리고 focusout(커서를 input 밖에서 클릭할때) 일때 실행되는 코드는 esc눌렀을떄 실행되는 코드랑 같다.

input.addEventListener("focusout", handleFocusOut);

const handleFocusOut = (event) => {
  if (event.target.lastKey === undefined) {
    // 커서가 바깥으로 가면 lastKey 가 undefined 되어버린다. 그럼 원상복귀다.
    const li = event.target.parentNode;
    const span = document.createElement("span");
    span.innerHTML = event.target.name;
    li.replaceChild(span, event.target);
    buttonRestorer(li);
  }
};

profile
'과연 이게 최선일까?' 끊임없이 생각하기

0개의 댓글