JavaScript로 To-Do List 만들기

janeljs·2020년 11월 21일
16
post-thumbnail

2일에 걸쳐 JavaScript를 배운 뒤 어떤 프로그램을 만들지 고민하다가 간단한 투두리스트를 구현해보기로 했다. 복습할 겸 HTML과 CSS부분도 처음부터 다 만들었기에 디자인이 예쁘지는 않지만 다 만들고 나니 뿌듯하다(삽질도 많이 했다).
다음에는 캘린더, 타임스탬프 등의 기능이 들어간 다이어리 앱을 개발해보고 싶다✨
👉🏻 완성된 투두리스트 페이지

구현하고 싶은 기능

구현하고 싶은 기능들은 다음과 같다.


  1. 입력창에 할 일을 입력하고 ➕버튼을 누르면 리스트에 할 일을 표시
  2. 할 일 오른쪽에 있는 ✖️버튼을 누르면 리스트에서 할 일 삭제하기
  3. 리스트에 표시된 할 일을 클릭하면 ✔️버튼이 왼쪽에 나타나면서 할 일에 줄 긋기

구현 방법

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>투두리스트</title>
    <link
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" 
      rel="stylesheet">
    <link rel="stylesheet" href="style.css">
</head>

우선 ! + tab을 이용하여 기본적인 HTML 문서 골격을 잡아주었고, title에는 투두리스트라고 적었다. 그 후, <link rel="stylesheet" href="style.css"> 태그를 통해 css 파일과 연결해주었다.

<div id="todolist">
    <div class="main__title">
        <h1>To do list</h1>
    </div>

    <div class="input__section">
        <form>
            <div>
                <input type = "text" class = "item" autofocus="true">
            </div>
            <div>
                <button type = "button" class="input__button"><i class="fas fa-plus-circle"></i></button>
            </div>
        </form>
    </div>

    <div class="item__list"></div>

</div>

<body>부분에는 todolist로 전체를 감싸는 wrapper를 만들고, 할 일을 입력하는 위에 섹션을 input__section으로 지정, 밑에 할 일들이 표시되는 부분을 item__list로 지정해주었다. 이번 프로그램에서는 form에서 받아온 데이터를 다른 페이지에 저장하지는 않았기에 action 속성은 생략해 주었다.

 <script src="main.js" defer></script>

body가 끝나기 바로 전에는 자바스크립트와 연결하는 <script>태그를 삽입하였고 defer라는 옵션을 주었다.

defer는 브라우저에게 document가 parse된 이후에 script가 실행되어야 한다고 알려주는 역할을 한다. async를 사용할 경우 정의된 스크립트의 순서와 상관없이 먼저 다운로드 된 스크립트가 실행되기 때문에 JavaScript파일들이 서로 의존적이고 정의된 순서에 따라 실행되어야 정상적으로 작동하는 경우 문제가 생긴다. 그러나 defer를 사용하면 HTML을 parsing하는 동안 필요한 JavaScript를 모두 다운로드 받은 후, 프로그래머가 정의한 순서대로 스크립트가 실행되기 때문에 위와 같은 문제를 예방할 수 있다.
출처: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script

JavaScript

'use strict';

let itemList = [];
let inputButton = document.querySelector(".input__button");
inputButton.addEventListener("click", addItem);
  1. 자바스크립트 코드는 strict mode로 작성하였고 우선 할 일들을 담을 배열을 선언해주었다.
  2. querySelector()를 사용하여 HTML에서 input__button 클래스를 가져와 inputButton 변수에 할당해주었다. var 대신 es6부터 적용된 let 구문을 사용하여 변수를 선언해주었다.
  3. addEventListener() 메소드로 버튼을 클릭하면 addItem() 함수가 실행되도록 설정하였다.
function addItem() {
    let item = document.querySelector(".item").value;
    if (item != null) {
        itemList.push(item);
        document.querySelector(".item").value = "";
        document.querySelector(".item").focus();
    }

    showList();
}
  1. 텍스트 타입의 인풋을 받는 item 클래스를 querySelector()를 이용하여 받아온 뒤 item 변수에 할당하였다. 지금 생각해보니 클래스와 변수명이 똑같아서 조금 헷갈릴 수도 있을 것 같다.
  2. item이 null이 아닐 때 itemList 배열에 item을 push했고, push 한 다음 item 클래스의 value는 지워주고 focus를 적용하여 커서가 깜박이게 설정하였다.

function showList() {
    let list = "<ul>"
    for (let i = 0; i <itemList.length; i++) {
        list += "<li>" + itemList[i] + "<span class='close' id=" + i + ">" + "\u00D7" + "</span></li>";
    }
    list += "</ul>";
    document.querySelector(".item__list").innerHTML = list;


    let deleteButtons = document.querySelectorAll(".close");
    for (let i = 0; i < deleteButtons.length; i++) {
        deleteButtons[i].addEventListener("click", deleteItem);
    }
}

function deleteItem() {
    let id = this.getAttribute("id");
    itemList.splice(id, 1);
    showList();
}
  1. 1번 기능을 구현하기 위해 showList()함수를 만들었다.

    1번 기능 입력창에 할 일을 입력하고 ➕버튼을 누르면 리스트에 할 일을 표시

  2. 먼저 for문을 통해 배열에 들어온 요소를 ul의 형태로 list 변수에 담았고, innerHTML 속성을 사용하여 item__list 클래스에 list를 담았다.

    innerHTML은 element에 포함된 HTML 또는 XML 마크업을 가져오거나 설정한다.
    출처: https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML

  3. querySelectorAll()을 활용해 클래스가 close인 모든 element를 가져와 deleteButtons에 저장한 뒤, deleteButtons 배열의 각 요소를 인덱싱해 x버튼이 클릭될 경우 해당 item이 삭제되도록 설정했다.

    2번 기능 할 일 오른쪽에 있는 ✖️버튼을 누르면 리스트에서 할 일 삭제하기

  4. splice 함수를 사용하면 지정한 인덱스(id)부터 지정한 숫자(1)만큼 요소를 삭제할 수 있다.

let checkList = document.querySelector('.item__list');
checkList.addEventListener('click', event => {
  if (event.target.tagName === 'LI') {
    event.target.classList.toggle('checked');
  }
});
  1. 3번 기능 구현을 위해 클래스가 item__list인 요소를 가져와 checkList 변수에 저장했다.
  2. classList의 toggle 메서드를 사용하여 click 이벤트가 발생했을 때 checked 클래스가 존재한다면 제거하고, 존재하지 않으면 추가하도록 설정했다.

3번 기능 리스트에 표시된 할 일을 클릭하면 ✔️버튼이 왼쪽에 나타나면서 할 일에 줄 긋기

CSS

CSS는 전체 코드를 다 다루기 보다는 (CSS 접한지 2주도 안 된 초보자 기준) 모를 수도 있을 만한 부분만 설명하려고 한다.

깜박이는 커서 색상 바꾸기

caret-color: #02aab0;

배경 그라데이션 주기

background: linear-gradient(135deg, #02aab0, #00cdac);

클릭했을 때 테두리 생기는 것 방지하기

*:focus {
  outline: none;
}

❎ 버튼 위치 조정

ul li {
  cursor: pointer;
  position: relative;
  ...
}
.close {
  position: absolute;
  ...
}

버튼을 만들고 position을 absolute로 지정했다면 반드시 버튼이 속해있는 상위 box의 position을 relative로 바꿔주어야 한다. position을 absolute로 지정한 아이템과 가장 가까이에 있는 부모 중 기본값이 static이 아닌 부모를 기준으로 아이템의 위치가 정해지기 때문이다.

체크 및 가운데 선 표시하기

ul li.checked {
  background: #ddd;
  color: #272341;
  text-decoration: line-through;
}

ul li.checked::before {
  content: "";
  position: absolute;
  border-color: #272341;
  border-style: solid;
  border-width: 0 2px 2px 0;
  top: 13px;
  left: 80px;
  transform: rotate(45deg);
  height: 15px;
  width: 7px;
}

위의 코드를 보면 알 수 있듯이 체크 표시는 약간의 트릭을 통해 만든 것이다. 오른쪽과 아래에만 border을 지정한 뒤 회전시켜 체크 표시를 만들었다. 다 한 일에 줄을 긋고 싶다면 text-decoration을 line-through로 지정해주면 된다.

반응형으로 만들기

@media screen and (max-width: 768px) {...}

768px보다 더 작은 화면에 대해서는 미디어 쿼리를 적용해주었다. 개발자 도구의 toggle device toolbar(Ctrl+Shift+M)을 이용하면 모바일 환경에서 어떻게 보이는지 확인할 수 있다.

간단한 기능 구현에 디자인도 부족한 부분이 많지만, 최대한 초보자들이 따라 만들기 쉽도록 자세하게 작성해보았다. To-Do List를 구현한다고 구글링을 많이 해봤지만, 코드+약간의 주석만 있는 경우가 많아 책, 블로그, mdn 등을 돌아다니며 정보를 모아야했다. 이 포스팅이 JavaScript를 처음 접하는 사람들에게 도움이 되기를 바라며 글을 마친다.
👉 전체 코드 확인하기


참고 서적
Do it! 웹 프로그래밍을 위한 자바스크립트 기본편 (고경희 지음)

3개의 댓글

comment-user-thumbnail
2021년 6월 15일

깔끔하고 쉬운 설명감사해요! todolist만드는데 큰 도움이 되었습니다 :-)!!

1개의 답글