<form id="todo-form">
<input type="text" placeholder="Write a To Do and Press Enter." required>
</form>
<ul id="todo-list"></ul>
const toDoForm = document.getElementById("todo-form");
const toDoInput = toDoForm.querySelector("input");
// = document.querySelector("#todo-form input")과 같음
const toDoList = document.getElementById("todo-list");
HTML 아이디를 가져와 toDoForm과 toDoList, toDoInput 변수를 생성.
function handleToDoSubmit(event){
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value = "";
paintToDo(newTodo);
}
toDoForm.addEventListener("submit", handleToDoSubmit);
toDoForm에 이벤트리스너를 생성해 submit 되었을 때 function handleToDoSubmit()이 실행되게 함
function handleToDoSubmit에는 가장 먼저, 브라우저에서 자동으로 실행되는 submit이벤트를 preventDefault로 없애준다
toDoInput의 값(value)을 newTodo 변수로 생성한 후, 입력하고 엔터를 누르면(submit) 공백이 되도록 설정
위 모든 것이 실행 된 후,paintToDo(newTodo);가 실행되게 함
function paintToDo(newTodo){
const li = document.createElement("li");
const span = document.createElement("span");
span.innerText = newTodo;
const button = document.createElement("button");
button.innerText = "❌";
li.appendChild(span);
li.appendChild(button);
toDoList.appendChild(li);
button.addEventListener("click", deleteToDo);
}
function paintToDo(newTodo)에서는 document.createElement("태그");를 통해 HTML에 요소를 넣어주는 기능을 사용(HTML에 태그를 넣기 위해서는 반드시 document.로 입력)
변수 li와 span,button을 변수로 설정 후, document.createElement로 각각 <li> </li> 와 <span> </span>, <button> </button>이 HTML에 들어갈 수 있게 함
span의 innerText에는 newToDo의 값, 즉, toDoInput.value가 들어가고
button의 innerText에는 삭제할 수 있는 모양인 x이모지를 넣어준다
span은 li의 자식이 되어야 하니 li.appendChild(span);으로 li 밑에 span을 넣어주고 그 밑에 들어가는 버튼 또한 마찬가지로 넣어준다.(span과 button을 순서대로 넣음)
li태그 또한 마찬가지로 위에서 변수로 생성한 toDoList의 자식으로 들어갈 수 있게 함
(appnedChild는 맨 마지막에 들어가야 한다)
button을 클릭하면 삭제할 수 있는 이벤트리스너 생성하여 function deleteToDo가 실행되도록 함
function deleteToDo(event){
const li = event.target.parentElement;
li.remove();
}
그냥 button을 클릭하여 삭제하면 li가 여러개 생성됐을 때 어떤 것인지 컴퓨터는 알 수 없음
따라서, event.target.parentElement;를 사용하여 li변수를 설정하고
버튼과 함께 있는 li를 지정하여 선택한 li가 삭제될 수 있게 li.remove()함
const TODOS_KEY = "todosKey"
const toDos = [];
function saveToDos(){
//localStorage.setItem("todosKey", toDos);
localStorage.setItem(TODOS_KEY, JSON.stringify(toDos));
//JSON.stringify(변수명) -> 자바스크립트의 값이나 객체(array등)를 스트링으로 변환
//JSON.parse("스트링") -> 단순한 스트링을 자바스크립트가 이해할 수 있는 array로 변환
}
투두리스트들의 값들을 저장하기 위해서는 localStorage를 사용.
이 값들을 array로 만들어야 하기때문에 toDos라는 단순한 array 변수를 생성함
(왜? 다음날 깨달았는데 만들고 있는 것은 투두리스트이니 배열이 필요한 것이 당연..)
function saveToDos() 함수를 생성하여
localStorage.setItem("키값이름", 값Value); 을 만드는데 이때, "todosKey"는 변수 TODOS_KEY로 변경 가능
JSON.stringify와 JSON.parse의 역할은 위 코드의 주석과 밑에 예시 보기
localStorage는 array를 저장할 수 없다. 오직 텍스트만 저장 가능. 위에서 array를 넣었으나 이는 localStorage에서는 그저 array처럼 보이는 텍스트일 뿐이다. 그래서 우리는 자바스크립트의 메소드인 JSON.parse()와 JSON.stringify()를 이용하는 것이다.
ex) value = a,b,c,d
const toDos = [ ];
localStorage.getItem("todosKey")의 결과 -> "[\"a",\"b\",\"d\",\"d\"]"
JSON.stringify(toDos)의 결과 -> "[\"a",\"b\",\"d\",\"d\"]"
이렇게 array처럼 보이게 문자를 만들어 준 후, 진짜 array 변경을 위해서
JSON.parse를 사용한다.
JSON.parse(localStorage.getItem("todosKey"))의 결과 -> ["a", "b", "c", "d"]
살아있는 array로 완성
이후 위쪽에 TODOS_KEY라는 변수를 생성하여 todosKey를 여러번 사용할 때 발생할 수 있는 오타를 줄여준다(자바스크립트는 스트링이 아닌 변수의 오타만 알려주기 때문)
// function handleToDoSubmit(event){
// event.preventDefault();
// const newTodo = toDoInput.value;
// toDoInput.value = "";
toDos.push(newTodo);
// paintToDo(newTodo);
saveToDos();
// }
위에 썼던 function handleToDoSubmit(event)에는 두 기능이 더 추가되어야 하는데,
하나는 paintToDo(newTodo)가 실행되기 전에 newTodo를 toDos(위에서 array로 설정한 변수)에 push하여 넣어주어야한다.
이후, paintToDo(newTodo)가 실행되고 이를 저장해주는 function saveToDos()가 실행되도록 함
toDoForm.addEventListener("submit", handleToDoSubmit);
const savedToDos = localStorage.getItem(TODOS_KEY);
if(savedToDos !== null){
const parsedToDo = JSON.parse(savedToDos);
parsedToDos.forEach (paintToDo);
}
위쪽에 썼던 toDoForm.addEventListener("submit", handleToDoSubmit); 밑에 변수 saveToDos를 생성
만약 localStorage에 저장된 값이 있다면(!== null)
saved된 ToDo들을 변수로 설정 후 그 값을 parse 즉, array 시켜주는 것을 다시 변수로 설정
forEach()는 array에 있는 각각의 item에 대해 function 을 실행할 수 있게 해줌
ex)
value = a,b,c,d
function sayHello(){
console.log("hello");
}
parsedToDos.forEach(sayHello);
위에 것을 실행시키면 콘솔창에는 parsedToDos가 가진 array의 item수만큼 hello갯수가 찍힘
----------------------------------------------------> 4️⃣ hello
forEach는 item들에 대해 한 개의 function만 실행시켜주기때문에 function을 처리하고 있는 item이 어떤 것인지 알 수 없음
function sayHello(item){
console.log("hello", item);
}
parsedToDos.forEach(sayHello);
그래서 우리는 브라우저가 가진 item기능을 이용하여 출력을 함
hello a
hello b
hello c
hello d
이 기능에서는 최종적으로 parseToDos에서 forEach된 값들을 paintToDo에 넣어준다
여기까지만 하면 localStorage에 Value들은 새로고침을 해도 없어지지 않지만 여기에 추가로 Value를 입력한 후, 새로고침을 하면 이전의 Value들이 사라지는 오류가 발생
이유는 const toDos=[ ];가 항상 비어있고, toDos.push(newToDo)가 발생하면서 계속 새로운 array가 생기기때문에 이전의 Value들을 덮어쓰면서 사라진 것.
이를 해결하기 위해서는 const toDos=[ ];를 let toDos=[ ];로 변경.
if(savedToDos !== null){
const parsedToDo = JSON.parse(savedToDos);
parsedToDos.forEach (paintToDo);
}
위 코드에서 const parsedToDo = JSON.parse(savedToDos); 밑에 toDos = parsedToDos; 코드 추가
이렇게 하면 더 이상 toDos의 array는 const toDos=[ ];의 빈 array를 계속 저장하는 것이 아닌 이전의 모든 element들을 가지고 있는 array를 저장하게 됨
여기까지 하면 Value를 추가해서 새로고침을 해도 계속 가지고 있지만 삭제한 후, 다시 새로고침을 하면 삭제했던 값들이 다시 생겨남. 이는 HTML에서는 어떤 li를 삭제해야 하는지 알고있지만 localStorage에서는 알 수 없기때문에 발생
이를 해결하기 위해서는 array 아이템들에게 각각의 랜덤 Id를 부여한다.
밑에 코드 참조
function handleToDoSubmit(event){
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value = "";
const newTodoObj = {
text: newTodo,
id: Date.now()
}
toDos.push(newTodoObj); //아이디 부여를 위해 push를 텍스트만 하는 것이 아닌 아이디까지 부여
paintToDo(newTodoObj);
saveToDos();
}
Date.now() 는 1000분의 1초 값을 반환함 이를 랜덤 아이디로 사용
newTodoObj를 toDos array에 넣는다
paintToDo에 스트링으로 newToDo를 주는 것 대신에 newTodoObj를 넣어준다
그리고 value를 보여주기 위해선 기존에 paintToDo가 받았던 text 대신에 object를 받아야 하기때문에 수정이 필요함
따라서 function paintToDo(newTodo)에서 span.innerText = newToDo.text;로 변경
li.id = newToDo.id; 코드 넣기
이제 function deleteToDo(event)에서 삭제하면 아이디값을 가져온 것으로 그 값이 localStorage에서도 삭제하도록 만들거임
array에서 item 지운다는 것은 사실 지우고 싶은 item을 빼고, 새 array를 만드는 것임
filter함수를 사용. forEach와 비슷하지만 filter함수는 반드시 true값을 반환한다.
만약 false를 리턴하면 그 item은 새 array에 포함되지 않는 원리
ex)
function sexyFilter(item){return item !== 3}
[1, 2, 3, 4, 5].filter(sexyFilter)
위 결과는 [1, 2, 4, 5]가 array됨
왜냐하면 1,2,4,5는 모두 3이 아닌 것이 true가 되기때문에 3 !== 3 만 false이므로 false가 아닌 나머지 true값들은 모두 반환
또다른 예시 사진(사진처럼 text도 가능)
여기까지 이해했다면 이제, todos array를 업데이트 해야함. 즉, localStorage에 저장된 array를 지우고 새 array를 넣는 것으로 마치 저장소에서 삭제한 것처럼 보이게 만듬
deletToDo(event)에서도 업데이트(?)를 해야함
//function deleteToDo(event){
// const li = event.target.parentElement;
// li.remove();
toDos = toDos.filter(toDo => toDo.id !== parseInt(li.id));
//
//}
위 코드를 추가함
arrow function을 사용하여 toDo의 아이디가 li의 아이디가 아닐 때 값을 반환
즉, 우리가 클릭한 li.id와 다른 toDo는 남겨두기 위함
이때, !== li.id만 한다면 이는 string값으로 나옴 id값에 있는 것은 number
따라서 우리는 id값의 숫자를 받기 위해 parseInt()함수를 통해 문자열을 숫자로 바꾸어줘야 함
function sayHello(item){
console.log("hello", item);
}
parsedToDos.forEach(sayHello);
위 문장을 많은 function을 만드는 대신에 줄여서 쓸 수 있는데 이를 arrow function 이라 함.
parsedToDos.forEach ((item) => console.log("hello", item));
변수명을 입력할 필요도, function을 입력할 필요도 없음
두 방식의 기능 차이나 속도 등 아무 상관 없으니 편한 것으로 쓰기
const toDoForm = document.getElementById("todo-form");
const toDoInput = toDoForm.querySelector("input");
const toDoList = document.getElementById("todo-list");
const TODOS_KEY = "todosKey"
let toDos = [];
function saveToDos(){
localStorage.setItem(TODOS_KEY, JSON.stringify(toDos));
}
function deleteToDo(event){
const li = event.target.parentElement;
li.remove();
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.innerText = newTodo.text;
const button = document.createElement("button");
button.innerText = "❌";
button.addEventListener("click", deleteToDo);
li.appendChild(span);
li.appendChild(button);
toDoList.appendChild(li);
}
function handleToDoSubmit(event){
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value = "";
const newTodoObj = {
text: newTodo,
id: Date.now()
}
toDos.push(newTodoObj);
paintToDo(newTodoObj);
saveToDos();
}
toDoForm.addEventListener("submit", handleToDoSubmit);
const savedToDos = localStorage.getItem(TODOS_KEY);
if(savedToDos !== null){
const parsedToDos = JSON.parse(savedToDos);
toDos = parsedToDos;
parsedToDos.forEach(paintToDo);
}