form은 로그인폼과 마찬가지로 submit 이벤트를 가지며 브라우저의 기본동작(페이지 새로고침)이 있으므로 이를 event.preventDefault() 로 기본값을 없애준다.
li 옆에 삭제 버튼을 만들 예정이므로 li안에 span을 만들어준다.
li > span, button
const toDoForm = document.querySelector("#todo-form");
const toDoInput = toDoForm.querySelector("input");
const toDoList = document.querySelector("#todo-list");
function paintToDo(newTodo) {
const li = document.createElement("li");
const span = document.createElement("span"); // span을 이용해서 li 만들기
li.appendChild(span); // li 내부에 span 넣기(html 요소 만들기)
span.innerText = newTodo;
toDoList.appendChild(li); // ul 내부에 li 넣기 -----> 문제점: 새로고침하면 투두리스트 사라짐, 지우기 버튼 없음
}
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value; //인풋의 현재 값을 새로운 변수에 복사하는 것.
toDoInput.value = ""; // 엔터누를때마다 인풋 비어있게 하기. newTodo 변수와 전혀 상관 없음.
paintToDo(newTodo);
}
toDoForm.addEventListener("submit", handleToDoSubmit);
새로고침하면 리스트가 사라지며, 리스트 삭제 버튼이 없다는 문제가 있다.
삭제버튼을 눌러도 어떤 li를 삭제하려고 클릭한건지 그 정보를 알 수 없다.
삭제할 요소의 경로를 추적해야 한다. 클릭된 button이 어떤 건지에 대한 단서를 얻어야 한다.
button.addEventListener("click", deleteToDo); 에서 삭제버튼이 클릭되면 함수를 실행한다.
이때 click event에 대한 정보를 가져올 수 있다.(중요)
클릭이벤트 정보를 살펴보면 이벤트의 property 중 하나인 path 에서 element(버튼)의 parentElement 즉, 부모요소(li)를 찾을 수 있다.
이벤트 property 중 하나인 target: button 이라는 것도 알 수 있다.
target을 살펴보면(console.dir(event.target)) target의 부모요소 parentElement: li 혹은 parentNode: li 이 확실해진다.
이제 console.dir(event.target.parentElement.innerText); 를 통해 어떤 li가 클릭되었는지 알 수 있다.
정리하자면 event에 대한 정보를 가지고 활용한다. target은 클릭된 html element다. 모든 html element는 하나 이상의 property를 가진다. parentElement는 클릭된 element의 부모다. 여기서 어떤 li가 클릭되었는지 알 수 있다.
function deleteToDo(event) { const li = event.target.parentElement; li.remove(); } button.addEventListener("click", deleteToDo);x버튼을 클릭하면 event를 얻게 된다. event는 target을 준다. target은 button이다. button은 부모를 갖고 있고 부모에 접근할 수 있다.(li) 그 li를 제거한다.
페이지 새로고침 시 입력했던 투두리스트가 사라지는 문제점이 발생한다. 값을 입력하면 localStorage에 저장되고 새로고침하면 입력한 값을 화면에 불러오도록 한다.
step 1. todo 저장하기
todos array를 localStorage에 저장해준다. 그러나 localStorage에는 array를 저장할 수 없다. 오직 문자열로만 저장할 수 있다.
그러나 array 자체를 문자열로 만들기 위해서 아래 방법을 사용한다. 추후에 localStorage에서 불러올 때 문자열을 array로 만들기 위함이다.
JSON.stringify(): js object나 array나 어떤 것이든 string data type으로 바꿔주는 기능
----> 인풋값을 직접 localStorage 에서 확인해봤을 때 값들을 단순한 string 으로 변경하여 array 모양으로 저장한다.
ex) JSON.stringify([1,2,3,4]) ---> "[1,2,3,4]" 반환JSON.parse(): string data type을 object로 바꿔준다. ---> 이 경우 무언가 할 수 있는 살아있는 array을 얻을 수 있다.
ex) JSON.parse("[1,2,3,4]") ----> [1,2,3,4] 반환
step 2. todo 불러오기 (새로고침하면 리스트가 사라지는 것 해결)
< false value>
- false
- 0
- -0
- " " (빈 string)
- null
- undefined
- NaN
forEach(): js는 array 안의 각각의 item에 대해 하나의 function을 실행할 수 있게 해준다.(주로 화살표 함수식 작성), 단 함수를 배열안의 아이템 수만큼 실행하는 것은 아니다.
입력값은 저장되는데 새로고침 후 값을 넣으면 예전 것들이 사라지고 새로 추가한 것만 localStaorage에 저장된다. 그 이유는 입력값을 push하는 배열이 [ ]로 항상 비어있기 때문이다.
application이 시작될 때 toDos array를 빈 값으로 시작하는 대신에
const를 let으로 바꿔서 업데이트가 가능하도록 만들고, localStaorage에 toDo들이 있으면 toDos에 parsedToDos를 넣어서 전에 있던 toDo들을 복원한다.
li에 id 부여하기
그러나 또 문제가 있다.
리스트를 지우면 화면에서는 지워지지만 새로고침 하면 다시 화면에 나타난다. localStaorage에서는 지우지 않았기 때문이다.
localStaorage는 데이터베이스(array)는 아니다. 그저 array 값을 복사해두는 곳이다.
값을 지울 때 어떤 값을 지우는지 구분이 안된다.
todo에 id를 부여하여 obj를 만들어준다. [{id:12415, text="dfas"}] 처럼.
Date.now() (밀리초를 제공)를 사용하여 랜덤으로 id를 만들어준다.
지우고 싶은 item을 제외하고 새 array를 만든다.
filter() : array 내 각 item에 대하여 true를 return하는 함수, flase 반환시 그 item은 새 array에 포함되지 않는다. 새 array를 준다.
function myFilter() {return true}
ex) const arr = [1,2,3,4,5];
function myFilter(item) {return item !== 3};
arr.filter(myFilter) // 새로운 배열 [1,2,4,5] 반환 (기존 arr는 그대로)
array의 각 item을 myFilter의 첫번째 인자로 전달해 준다.
그럼에도 지워지지 않는 이유는 변수의 타입(typeof)때문. toDo.id는 number고 li.id는 string이다. 서로 비교될 수 있도록 문자열을 숫자로 바꿔준다(parsInt())
const toDoForm = document.querySelector("#todo-form");
const toDoInput = toDoForm.querySelector("input");
const toDoList = document.querySelector("#todo-list");
const TODOS_KEY = "todos";
let toDos = [];
function saveToDos() {
localStorage.setItem(TODOS_KEY, JSON.stringify(toDos)); //array 를 저장 console.log ['a', 'b', 's'] localStorage a,b,c -->JSON.stringify적용 후 console.log ['f', 'd', 's'] localStorage["f","d","s"]
}
function deleteToDo(event) {
const li = event.target.parentElement; // 삭제하고 싶은 li
li.remove(); // li 삭제
toDos = toDos.filter((toDo) => toDo.id !== parseInt(li.id));
saveToDos();
}
function paintToDo(newTodo) {
const li = document.createElement("li");
li.id = newTodo.id;
const span = document.createElement("span"); // span을 이용해서 li 만들기
span.innerText = newTodo.text;
const button = document.createElement("button");
button.innerText = "X";
button.addEventListener("click", deleteToDo);
li.appendChild(span); // li 내부에 span 넣기(html 요소 만들기)
li.appendChild(button); //append는 맨 마지막에 놓여지므로 button은 span 뒤에 놓인다.
toDoList.appendChild(li); // ul 내부에 li 넣기 -----> 문제점: 새로고침하면 투두리스트 사라짐, 지우기 버튼 없음
}
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value; //인풋의 현재 값을 새로운 변수에 복사하는 것.
toDoInput.value = ""; // 엔터누를때마다 인풋 비어있게 하기. newTodo 변수와 전혀 상관 없음.
const newTodoObj = {
text: newTodo,
id: Date.now(),
};
toDos.push(newTodoObj); // 배열에 obj 저장
paintToDo(newTodoObj); // todo 항목 표시하기,
saveToDos();
}
toDoForm.addEventListener("submit", handleToDoSubmit);
const savedToDos = localStorage.getItem(TODOS_KEY);
if (savedToDos !== null) {
const parsedToDos = JSON.parse(savedToDos); //배열 반한
toDos = parsedToDos; // 이전 입력값 복원
parsedToDos.forEach(paintToDo); // parsedToDos의 각각의 item에 대하여 한개의 함수 실행(화살표함수),
}