HTML & CSS & JavaScript를 활용한 ToDoList

Twittler 과제를 하기 전에 NomadCoder님의 ToDoList를 복습하고 시작하려한다.
물론, 지금 내 실력으로 과제를 완성하기 힘든 것도 있으며,
좀 더 완성도 있는 과제를 만들기 위해서이다...!👏🏻👏🏻👏🏻
사실 벌써 3번들었는데 또 까먹었다.그렇다.
(Youtube검색만 하면 바로 나오는 무료! 인기! 강의다...!!)


1. 파일 구성

기본적으로 필요한 파일을 구성한다.
index.html , index.css, clock.js 를 생성한다.

2. html 작성

!+tab emmet을 이용하여 기본을 작성하고, 각 파일을 연결한다.
이후, 시간을 표시하기 위해 js-clock클래스에 '00:00:00'을 추가한다.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>CLOCK</title>
  <link rel="stylesheet" href="./index.css">
</head>
<body>
  <div class="js-clock">
    <h1>00:00:00</h1>
  </div>
  <script src="./clock.js"></script>
</body>
</html>

3. javascript 작성

한 번에 많은 양을 작성하지만 그렇게 어렵지는 않다. 천천히 보자.

// 1. 클래스와 h1태그를 가져온다.
const clockContainer = document.querySelector(".js-clock");
const clockTitle = clockContainer.querySelector("h1");
// 2. 현재 시간을 가져올 함수이다.
function getTime() {
  const date = new Date();
  const hours = date.getHours();
  const minutes = date.getMinutes();
  const seconds = date.getSeconds();
  // 3. 각 자리가 10이상일 경우 숫자 앞에 0을 표기한다.
  clockTitle.innerText = `${hours < 10 ? `0${hours}` : hours}:${minutes < 10 ? `0${minutes}` : minutes}:${seconds < 10 ? `0${seconds}` : seconds}`;
}
// 4. initializer로 함수를 모아서 실행한다.
function init() {
  getTime();
  setInterval(getTime, 1000);
}

init();

html 파일을 실행하여 잘 실행되는지 확인한다.
image.png


4. html에 이름입력창을 생성한다.

form태그에 생성하고, greeting.js파일을 생성하고 연결한다.

<body>
  <!-- 생략 -->
  <form class="js-form form">
    <input type="text" placeholder="이름을 입력하세요..." />
  </form>
  <h4 class="js-greetings greetings"></h4>
  <script src="./greeting.js"></script>
  <!-- 생략 -->
</body>

5. greeting.js를 작성한다.

localStorage에 이름을 저장하여 인사 메세지를 출력한다.
함수 선언은 아래에서 위로 보는 것이 이해하기 편하다.

// 각 클래스를 가져온다.
const form = document.querySelector(".js-form");
const input = form.querySelector("input");
const greeting = document.querySelector(".js-greetings");
// 중복되는 상수를 따로 선언한다.
const USER_LS = "currentUser";
const SHOWING_CN = "showing";
// 이름을 저장하는 함수
function saveName(text) {
  localStorage.setItem(USER_LS, text);
}
// form태그의 이벤트리스너 함수
function handleSubmit(e) {
  e.preventDefault();
  const currentValue = input.value;
  paintGreeting(currentValue);
  saveName(currentValue);
}
// showing태그를 생성하고, 이벤트리스너를 생성한다.
function askForName() {
  form.classList.add(SHOWING_CN);
  form.addEventListener("submit", handleSubmit);
}
// form태그를 지우고, Hello ~ 문구를 출력한다.
function paintGreeting(text) {
  form.classList.remove(SHOWING_CN);
  greeting.classList.add(SHOWING_CN);
  greeting.innerText = `Hello ${text}`;
}
// LocalStorage에 이름이 있으면, 이름을 불러온다.
function loadName() {
  const currentUser = localStorage.getItem(USER_LS);
  if(currentUser === null) {
    askForName();
  } else {
    paintGreeting(currentUser);
  }
}
// initializer
function init() {
  loadName();
}

init();

간단한 CSS 몇가지만 추가한다.

/* index.css */
.form,
.greetings {
  display: none;
}
.showing {display: block;}
.body {color: #34495e;}

image.png


6. todo폼 작성

html파일에 폼을 생성하고, input을 생성한다.

<body>
  <!-- 생략 -->
  <form class="js-toDoForm">
    <input type="text" placeholder="할 일을 입력하세요...!" />
  </form>
  <ul class="js-toDoList"></ul>
  <script src="./todo.js"></script>
  <!-- 생략 -->
</body>

7. todo.js 작성

// form, input, ul 태그를 가져옴.
const toDoForm = document.querySelector(".js-toDoForm");
const toDoInput = toDoForm.querySelector("input");
const toDoList = document.querySelector(".js-toDoList");
// 로컬스토리지 상수 생성
const TODOS_LS = "todos";
// 객체를 담을 배열 생성
let toDos = [];

// 삭제 메소드 구현
function deleteToDo(event) {
  const btn = event.target;
  const li = btn.parentNode;
  toDoList.removeChild(li);
  const cleanToDos = toDos.filter(function(toDo) {
    return toDo.id !== parseInt(li.id);
  });
  toDos = cleanToDos;
  saveToDos();
}

// 로컬스토리지에 toDos 저장 (문자열로 저장해야함.)
function saveToDos() {
  localStorage.setItem(TODOS_LS, JSON.stringify(toDos));
}

// ul태그에 span, button이 담긴 ul 생성.
function paintToDo(text) {
  const li = document.createElement("li");
  const delBtn = document.createElement("button");
  const span = document.createElement("span");
  const newId = toDos.length + 1;
  delBtn.innerText = "❌";
  delBtn.addEventListener("click", deleteToDo);
  span.innerText = text;
  li.appendChild(span);
  li.appendChild(delBtn);
  li.id = newId;
  toDoList.appendChild(li);
  // 로컬스토리지에 저장할 toDoObj 객체를 생성
  const toDoObj = {
    text: text,
    id: newId
  }
  toDos.push(toDoObj);
  saveToDos();
}
// toDoForm의 이벤트리스너 생성
function handleSubmit(e) {
  e.preventDefault();
  const currentValue = toDoInput.value;
  paintToDo(currentValue);
  toDoInput.value = "";
}
// 로컬스토리지에서 TODOS_LS를 불러옴
function loadToDos() {
  const loadedToDos = localStorage.getItem(TODOS_LS);
  if(loadedToDos !== null) {
    const parsedToDos = JSON.parse(loadedToDos);
    parsedToDos.forEach((toDo) => {
      paintToDo(toDo.text);
    });
  }
} 

function init() {
  loadToDos();
  toDoForm.addEventListener("submit", handleSubmit);
}

init();

image.png

8. 배경이미지 삽입 & bg.js 작성

img 폴더를 생성하고, 원하는 배경이미지를 1,2,3,4,5 이름으로 넣는다.
그리고, bg.js파일을 생성하고, html에서 불러온다.
이후 bg.js를 작성한다.

// bg.js

const body = document.querySelector("body");
// 이미지 카운트
const IMG_NUMBER = 5;
// 배경 이미지를 body에 그림
function paintImage(imgNumber) {
  const image = new Image();
  image.src = `img/${imgNumber + 1}.jpg`;
  image.classList.add("bgImage");
  body.prepend(image);
}
// 랜덤한 숫자를 가져옴
function genRandom() {
  const number = Math.floor(Math.random() * IMG_NUMBER);
  return number;
}

function init() {
  const randomNumber = genRandom();
  paintImage(randomNumber);
}

init();

9. css 수정.

배경에 가려서 글자가 안보여 불투명하게 수정하였다.
약간의 html 수정도 필요하다.
css 전체 파일을 올려놓는다.

.form,
.greetings {
  display: none;
}
.showing {display: block;}
.body {background-color: #2c3e50;}
* {color: white;}

/* 페이드인 효과 */
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* 배경 이미지 */
.bgImage {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  animation: fadeIn 0.5s linear;
}
/* 배경 불투명 및 기타 작업 */
.wrap {padding: 10px; background-color: rgba(0, 0, 0, 0.4)}
form > input {color: black;}
ul > li {list-style: none;}
ul > li > button { background: none; border: none;}

10. 최종 결과

원래 날씨도 받아서 오지만, 필요하다면 추후 업데이트하겠다 :)
image.png