노마드코더 바닐라 JS로 크롬 앱 만들기 강의를 듣고 정리한 기록입니다. 아래 내용은 7.0 - 7.8 에 해당합니다.
할 일을 적어줄 input
태그는 form
안에 넣고, 할 일 목록을 담아줄 ul
태그를 만듭니다. ul
태그 안의 li
태그는 이후 js
로 만들어서 넣습니다.
<form id="todo-form">
<input type="text" placeholder="Write a To Do and Press Enter" required />
// reaquired 는 필수라는 것
</form>
<ul id="todo-list"></ul> // 이 안의 li 는 js 로 추가한다.
document.getElementById()
document.querySelector()
를 사용해 form
ul
input
태그를 js
파일에서 선택합니다. addEventListener()
로 form
태그의 submit
을 감지하고 함수를 실행합니다.
const toDoForm = document.getElementById("todo-form");
const toDoList = document.getElementById("todo-list");
const toDoInput = document.querySelector("#todo-form input");
function handleToDoSubmit(event) {
event.preventDefault(); // 기본 동작을 멈추고
const newTodo = toDoInput.value; // input 에 입력된 값을 변수에 할당하고
toDoInput.value = ""; // input 을 비워준다. 위에 변수 값이 0이 되는 것은 아님.
}
toDoForm.addEventListener("submit", handleToDoSubmit);
// 첫번째 인자, submit 를 확인하고 두번째 인자인 함수를 실행
handleToDoSubmit()
함수에 paintToDo()
함수를 추가하고(입력하면 화면에 보여주는 함수 실행), 해당 함수식을 위에 작성합니다. document.createElement()
로 html
태그를 생성하고 appendChild()
를 사용해 자식요소로 태그를 넣어줄 수 있습니다.
function paintToDo(newTodo) { // 텍스트가 들어갈 매개변수 설정
const li = document.createElement("li"); // li 태그 생성
const span = document.createElement("span"); // span 태그 생성
li.appendChild(span); // li 태그 안에 자식요소로 span 태그 넣기
span.innerText = newTodo; // span 태그 안에 input 값 넣기
toDoList.appenChild(li); // ul 태그에 위에서 만든 li 넣어주기
}
// li 를 만들고 span 을 만들고, li 안에 span 을 자식요소로 넣고, 그 span 안에 텍스트를 넣어준다. 텍스트는 아래 input 에서 받는다.
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value = "";
paintToDo(newTodo); // 위 함수로 input 값을 보낸다.
}
아래와 같이 html
에 태그가 추가되도록 js
코드를 수정합니다.
<li>
<span>text</span> // 텍스트를 이렇게 넣어주었었고,
<button>x</button> // 이렇게 버튼이 생성되어야 함 click event 를 기다리는
</li>
기존의 paintToDo 함수에 button 태그를 생성해서 넣어주면,
// 기존 코드
function paintToDo(newTodo) { // 텍스트가 들어갈 매개변수 설정
const li = document.createElement("li"); // li 태그 생성
const span = document.createElement("span"); // span 태그 생성
li.appendChild(span); // li 태그 안에 자식요소로 span 태그 넣기
span.innerText = newTodo; // span 태그 안에 input 값 넣기
toDoList.appenChild(li); // ul 태그에 위에서 만든 li 넣어주기
}
// 수정 코드
function paintToDo(newTodo) {
const li = document.createElement("li");
const span = document.createElement("span");
span.innerText = newTodo; // span 먼저 넣어주고
const button = document.creatElement("button"); // button 태그 생성
button.innerText = "X"; // 그 아래 버튼 생성하세 넣어주고
button.addEventListener("click", deleteToDo); // 클릭시 제거 함수 실행
li.appendChild(span); // 각각 li 안에 넣어주고
li.appendChild(button);
toDoList.appenChild(li); // li 를 ul 에 넣어준다!
}
이제 수정 코드 위에 deleteToDo()
함수를 만들어 줍니다. target
의 부모요소를 선택하는 parentElement
를 활용합니다.
function deleteToDo(event) {
const li = event.target.parentElement;
// 클릭한 button 의 부모 요소인 li 선택
li.remove(); // li 지우기
}
지금까지 코드로 할일을 생성하고 삭제하는 것이 가능하지만 새로고침하면 내용이 사라집니다. 그래서 입력한 데이터를 localStorage
라는 브라우저 저장소에 저장합니다. 우선 빈 배열을 만들어서 입력되는 값을 배열에 넣어줍니다.
//빈 배열을 만들고
const toDos = [];
//값이 입력될 때 그 값을 배열에 넣는다.
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value = "";
toDos.push(newTodo); // 입력된 값을 배열에 넣기.
paintToDo(newTodo);
}
이제 배열 내용을 localStorage
에 넣는 함수를 배열 아래쪽에 추가하고, 해당 함수를 handleToDoSubmit
에서 실행해주면,
function saveToDos() {
localStorage.setItem("todos", toDos)
// setItme 이 넣어주는 것, "todos" 는 데이터의 이름, 객체의 key 값과 비슷
}
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value = "";
toDos.push(newTodo); // 아래 실행하기 전에 배열에 값이 들어가 있는 상태
paintToDo(newTodo);
saveToDos(); // 이렇게 실행 코드 넣어준다!, 어레이를 로컬스토리지에 넣는 일
}
그리고 저장되는 데이터를 배열 형태로 만들어 주기 위해 JSON.stringify() 를 사용합니다.
function saveToDos() {
localStorage.setItem("todos", JSON.stringify(toDos));
}
저장된 값을 불러올 때는 localStorage.getItem()
을 사용하는데, 이때 문자열의 값을 배열로 가져오고 싶다면 JSON.parse()
를 사용합니다.
// 이런 식으로 쓸 수 있습니다.
JSON.parse(localStorage.getItem("todos"))
반복되는 "todos"
를 변수로 묶어주고, 입력된 값이 null
이 아닐 경우 (무언가 입력될 경우) 해당 값을 배열 형태로 가져와 화면에 보여줍니다. 배열을 돌면서 함수를 실행하는 forEach
도 활용합니다.
const TODOS_KEY = "todos" // 로컬스토리지에 저장된 데이터의 키값
const savedToDos = localStorage.getItem(TODOS_KEY);
if (savedToDos !== null) { // null 이 아닐 경우, 값이 입력될 경우
const parsedToDos = JSON.parse(savedToDos); //해당 변수에 배열 형태로 값 추가
parsedToDos.forEach(paintToDo); // 배열의 각 아이템들이 돌면서 함수 실행
}
여기까지 하면 const toDos = [];
배열이 항상 비어 있는 것으로 시작하고 newTodo
만 로컬스토리지에 저장하기 때문에 복사본이 저장되지 않습니다. 배열을 변경 가능한 변수로 선언하고 함수를 수정해보면,
let toDos = [];
if (savedToDos !== null) { // 비어있지 않다면,
const parsedToDos = JSON.parse(savedToDos);
toDos = parsedToDos // 추가한 코드, 기존에 작성된 값을 배열에 추가
parsedToDos.forEach(paintToDo);
}
이제 삭제버튼을 누르면 로컬스토리지에서 지워지고, 화면에서도 지워지게 해야 합니다.
우리가 쓰는 데이터베이스는 ToDos
라는 배열이고 로컬스토리지는 배열을 복사해두는 곳입니다. 화면에서는 어떤 html
을 지워야 하는지 알지만 로컬스토리지에서는 알 수 없습니다. 배열 안의 text
에 id
넘버가 있다면 로컬스토리지도 확인할 수 있습니다. 랜덤 숫자는 Date.now()
를 활용해서 줄 수 있습니다.
// 기존 코드
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value = "";
toDos.push(newTodo); // 여기서 텍스트 대신 오브젝트를 푸쉬하고 싶다.
paintToDo(newTodo);
saveToDos();
}
// 수정 코드
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value = "";
const newTodoObj = { // 객체를 선언하고 할당한다.
text: newTodo, // 객체 안의 value 로 값을 받고
id: Date.now(), // id의 value 로 랜덤 번호를 받는다.
}
toDos.push(newTodoObj); // 그렇게 받은 객체를 배열에 담는다.
paintToDo(newTodoObj); // 객체를 그리는 함수로 전달한다.
saveToDos(); // 배열로 전달된 객체를 로컬스토리지에 담는다.
}
paintTodo()
함수에도 매개변수로 객체가 들어가니 span 에 텍스트를 넣어주는 코드를 수정해야 하고, 입력되는 값의 id
넘버를 li
에도 부여해야 합니다.
function paintToDo(newTodo) {
const li = document.createElement("li");
li.id = newTodo.id; // li 에 입력값에 붙는 Id 숫자 값을 넣어준다.
const span = document.createElement("span");
span.innerText = newTodo.text; // 객체 안의 key 값으로 호출해야 함
const button = document.creatElement("button");
button.innerText = "X";
button.addEventListener("click", deleteToDo);
li.appendChild(span);
li.appendChild(button);
toDoList.appenChild(li);
}
li.id
값이 문자열이므로 비교를 위해 parseInt()
를 사용해 숫자로 바꾸어 주고, filter
를 사용해 해당 id 를 가진 데이터를 제외하고 배여을 만듭니다.
// 기존 코드
function deleteToDo(event) {
const li = event.target.parentElement;
li.remove();
}
// 수정 코드
function deleteToDo(event) {
const li = event.target.parentElement;
li.remove();
toDos = toDos.filter((toDo) => toDo.id !== parseInt(li.id));
// 입력되는 값의 id 와 li 의 id가 다른 것만 남긴다. 같으면 배열에서 제외한다.
saveToDos()
// 지우고 남은 배열을 로컬스토리지에 담아준다.
}
이렇게 하면 기능은 완성입니다.
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="todo-box">
<h1>My ToDoList</h1>
<form id="todo-form">
<input type="text" placeholder="Write To Do" required/>
<button>add</button>
</form>
<ul id="todo-list"></ul>
</div>
<script src="app.js"></script>
</body>
</html>
javascript
const toDoForm = document.getElementById("todo-form");
const toDoList = document.getElementById("todo-list");
const toDoInput = document.querySelector("#todo-form input")
let toDos = [];
const TODOS_KEY = "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 = "x";
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();
}
const savedToDos = localStorage.getItem(TODOS_KEY);
if (savedToDos !== null) {
const parsedToDos = JSON.parse(savedToDos);
toDos = parsedToDos;
parsedToDos.forEach(paintToDo);
}
toDoForm.addEventListener("submit", handleToDoSubmit);
그리고 CSS 스타일을 잡아줍니다.