노마드코더 Vanilla JS TODOLIST

소연·2021년 7월 5일
0

vanillaJS

목록 보기
8/8

HTML 코드

<form id="todo-form">
        <input type="text" placeholder="Write a To Do and Press Enter." required>
</form>

<ul id="todo-list"></ul>



TODOLIST 정리

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()함수를 통해 문자열을 숫자로 바꾸어줘야 함


localStorage부터 멘붕😫😥 다시 공부하기



arrow function

function sayHello(item){
console.log("hello", item);
}
parsedToDos.forEach(sayHello);


위 문장을 많은 function을 만드는 대신에 줄여서 쓸 수 있는데 이를 arrow function 이라 함.

parsedToDos.forEach ((item) => console.log("hello", item));

변수명을 입력할 필요도, function을 입력할 필요도 없음
두 방식의 기능 차이나 속도 등 아무 상관 없으니 편한 것으로 쓰기

TODOLIST JS

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);
}
profile
코린이🤪

0개의 댓글