index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="css/style.css" />
<title>Momentum App</title>
</head>
<body>
<form class="hidden" id="login-form">
<input
required
maxlength="15"
type="text"
placeholder="What is your name?"
/>
<input type="submit" value="Log In" />
</form>
<h2 id="clock">00:00:00</h2>
<h1 class="hidden" id="greeting"></h1>
<div id="quote">
<span></span>
<span></span>
</div>
<script src="js/greetings.js"></script>
<script src="js/clock.js"></script>
<script src="js/quotes.js"></script>
</body>
</html>
js/quotes.js 파일을 아래와 같이 작성
const quotes = [
{
quote: "The way to get started is to quit talking and begin doing.",
author: "Walt Disney",
},
{
quote: "Life is what happens when you're busy making other plans.",
author: "John Lennon",
},
{
quote:
"The world is a book and those who do not travel read only one page.",
author: "Saint Augustine",
},
{
quote: "Life is either a daring adventure or nothing at all.",
author: "Helen Keller",
},
{
quote: "To Travel is to Live",
author: "Hans Christian Andersen",
},
{
quote: "Only a life lived for others is a life worthwhile.",
author: "Albert Einstein",
},
{
quote: "You only live once, but if you do it right, once is enough.",
author: "Mae West",
},
{
quote: "Never go on trips with anyone you do ntot love.",
author: "Hemmingway",
},
{
quote: "We wander for distraction, but we travel for fulfilment.",
author: "Hilaire Belloc",
},
{
quote: "Travel expands the mind and fills the gap.",
author: "Sheda Savage",
},
];
const quote = document.querySelector("#quote span:first-child");
const author = document.querySelector("#quote span:last-child");
const todaysQuote = quotes[Math.floor(Math.random() * quotes.length)];
quote.innerText = todaysQuote.quote;
author.innerText = todaysQuote.author;
이제 지금까지 공부했던 내용들 기반으로 본격적인 todolist를 만들거다. 아자아자
<script src="js/todo.js"></script>
일단 todo.js라는 파일 하나 더 src폴더 안에다 만들어놓고 index.html에 script연결을 해주자.
이 화면에서 입력을 하고 엔터하면 submit이벤트 발생하면서 새로고침 되는데 그거 막기 위해서!?
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value; //비어있는 값을 변수에 다시 복사하는 것 뿐 변수의 기존 값이 없어지는 게 아니라는 것 명심
toDoInput.value = "";
paintToDo(newTodo);
}
이렇게 코드 추가해서 submit해도 새로고침 되지 않게 할 수 있고, 엔터를 누를 때마다 입력창이 비워지게 하기 위해서 toDoInput.value="";를 추가했다. 비우기 위한 코드다. 근데 명심할 건, 비웠다고 해서 newTodo의 값이 없어진 건 아니다. newTodo라는 변수에 비어있는 값을 복사했을 뿐 !
function paintToDo(newTodo) {
const li = document.createElement("li");
const span = document.createElement("span");
span.innerText = newTodo;
const button = document.createElement("button");
button.innerText = "❌";
button.addEventListener("click", deleteToDo);
li.appendChild(span);
li.appendChild(button);
toDoList.appendChild(li);
}
const li는 일단 꼭 li라는 이름으로 변수명을 지을 필요는 없음. 근데 "li"태그를 만들 때 사용할 변수인건데 이해하기 쉽게 하기 위해서 li라고 변수명을 지은 것임. span도 마찬가지 !
입력란에 hello라고 치고 엔터치면 console에는 아무것도 없다가 이렇게 li과 span이 만들어지는 걸 확인할 수 있다.
li태그와 span태그를 만들었고, span을 li 내부로 집어 넣었고, 텍스트를 span 내부에 넣은거지. 그리고 그 텍스트는 사용자가 form에서 나한테 준 newTodo값인거고 ! 그리고 새로운 li를 list(toDolist)에 추가해주게끔 코드를 짠거다. 그리고 지우는 기능을 구현하기 위해 button태그를 만들었고 button의 텍스트에 "X"을 innerText해줬어. 버튼도 li에 추가해줬어. 버튼을 click했을 때 삭제가 될 수 있게 addEventListener("click", deleteToDo)를 만들어줬고 deleteTodo()라는 함수는 아래와 같이 구현하면 됨.
function deleteToDo(event) {
const li = event.target.parentElement;
li.remove();
}
자, 그리고 click이 발생한 target을 정확히 모를 수 있는데 f12열어서 겁나 여러개의 프러퍼티들 중에서 path를 확인해보면 button이 타겟이 되고 있다는 걸 알 수 있음. 그래서 deleteToDo()를 이렇게 구현해준 거다. button의 부모노드인 li를 삭제해야 내가 추가한 그 리스트를 삭제 할 수 있는거니깐.
자 여기까지 했으면 일단 추가하고 삭제하는 기능까지는 완성이 됐는데 문제는 새로고침하면 내용들이 다 날아가버린다는 점이야. 이럴 땐 Local Storage에 저장하면 됨.
예를 들어 이렇게 a,b,c를 입력했을 때 다음 두 가지의 과정이 중요해.
먼저 저장을 하기 위해서 새로운 배열과 함수를 만들자.
const toDos = [];
function saveToDos() {
localStorage.setItem("todos", JSON.stringify(toDos));
}
local storage에는 텍스트형식만 저장할 수 있고 배열은 저장할 수 없었잖아? 근데 이렇게 JSON.stringify를 쓰면 내가 입력창에 입력한 것 그대로 텍스트화 시켜서 저장할 수 있게 해줌 !!! 매우 유용하니까 꼭 기억하장.
여기까지 하면 이제 내가 입력창에 입력한 todo내용이 문자열화돼서 그대로 local storage에 저장까지 잘 된다. 근데 로컬저장소에는 저장이 되는데 화면에는 나타나지 않는다. 그래서 새로고침을 하면 내용이 다 날아간 것 처럼 보임!! 이번에는 그걸 해결해보자.
if (savedToDos !== null) {
const parsedToDos = JSON.parse(savedToDos);
parsedToDos.forEach((item) => console.log("this is the turn of ", item));
}
일단 입력창에서 입력 받은 값이 null일 수도 있으니깐, 그게 아닌 경우에 대해서만 실행하게끔 조건문 걸어두고 이렇게 코드를 짰다.
forEach()는 괄호 속 함수를 실행시켜주는데, parsedToDos가 array잖아? 그 array에 있는 각각의 item에 대해서 실행시켜준다. 즉, parsedToDos에 있는 각각의 item에 대해서 console.log를 해주겠다는 뜻이다.
중요한 건, 내가 지금 어떤 item을 사용하고 있는지 모른다면 결국 무용지물이라는 것 !
자 슬슬 끝나가는데..
일단 내가 todos에 작성한게 로컬저장소에 잘 저장되고 새로고침해도 없어지지도 않게끔 잘 만들었다. 근데 문제가 생겼다 !! X버튼을 눌러서 리스트를 삭제해도 로컬저장소에는 그대로 남아있어서 새로고침을 하면 다시 나타난다는 점이다. 이를 해결하기 위해 다음과 같이 코드를 작성하자.
--> date()와 random을 이용해서 내가 입력한 각각의 todos들에 id를 부여하고 이를 통해서 원하는 내용을 로컬저장소에서 완전히 삭제하는 기능을 구현할 수 있다.
다음은 최종적으로 모든 기능을 구현한 todo.js 코드이다.
const toDoForm = document.getElementById("todo-form");
const toDoInput = document.querySelector("#todo-form input");
const toDoList = document.getElementById("todo-list");
const TODOS_KEY = "todos";
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);
}