본문 바로가기

JS

[JavaScript]투두 리스트 만들기

ToDoList 만들기

JS를 배우고 좀더 이해하고 친숙해지고자 투두 리스트를 만들어 보려고 합니다. 작업은 VSC(Visual Studio Code)로 했습니다. 파일들은 제 github홈페이지 에 저장해 놓을테니 참고해주세요. 이 코드가 꼭 정답이 아닙니다. 더 좋은 코드가 있을 수 있습니다.  

앱 디자인 구상

일단 투두 리스트 디자인을 구상해보았습니다. 심플한 디자인을 선호하는 편이라서 깔끔해보이게 디자인을 만들었습니다.

active는 "끝나지 않은 일" 이고 done은 "한 일" 입니다. 입력란에 해야 할 일을 입력하면 active라고 적혀있는 곳 밑에 리스트(active리스트라고 부르겠습니다.)를 출력해줍니다. active항목의 done 버튼을 누르게 되면 active리스트에서 그 항목이 없어집니다. 그리고 done이라고 적혀있는 곳 밑으로 출력됩니다. 또 done항목의 delete 버튼을 누르게 되면 완전히 없어지게 됩니다.
그렇다면 이번 프로젝트에서 구현해야하는 것은

  • 입력란에 해야 할 일을 입력하면 active리스트에서 보여준다.
  • active항목의 done버튼을 누르면...
    • active리스트에서 그 항목을 지운다.
    • done리스트에 그 항목을 추가한다.
  • done항목의 delete버튼을 누르면 그 항목을 지운다.

이렇게 될 수 있겠네요.

index.html

<!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">
    <link href="https://fonts.googleapis.com/css2?family=Raleway&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="style.css">
    <title>ToDoList</title>
  </head>
  <body>
    <!--주 제목-->
    <div class="mainTitle">ToDoList</div>

    <!--할일 정보 입력하는 곳-->
    <form action="#" class="addToDo" onsubmit="return addActiveList();">
      <input type="text" class="addToDoInfo" placeholder="Enter your to-do">
    </form>

    <!--투두리스트-->
    <div class="list">
      <!--투두리스트Active-->
      <div class="activeToDo">
        <div class="activeTitle">Active</div>
        <ul type="none" class="listActive">
        </ul>
      </div>

      <!--투두리스트Done-->
      <div class="doneToDo">
        <div class="doneTitle">Done</div>
        <ul type="none" class="listDone">
        </ul>
      </div>
    </div>
  </body>
  <script type="text/javascript" src="todo.js"></script>
</html>

글씨체는 구글폰트에서 제공하는 Raleway 를 썻습니다. 밑의 코드를 복사해서 <head> 안에 포함시켜서 사용할 수 있습니다.

<link href="https://fonts.googleapis.com/css2?family=Raleway&display=swap" rel="stylesheet">

input란에 값을 타이핑하고 엔터를 누르면 <form> 의 속성값인 onsubmit안에 있는 함수가 호출됩니다.

  <!--함수명에 원하는 함수이름을 적으면 됩니다.-->
  <form action="" onsubmit="return 함수명">
    <input type="text">
  </form>

저는 이렇게 코드를 짰습니다. onsubmit안에 있는 함수 addActiveList() 를 실행하면 투두리스트의 active리스트에 들어가게되는겁니다. 조금 이따가 작성할 todo.js에 addActiveList() 를 작성하기로 합시다.

<!--할일 정보 입력하는 곳-->
    <form action="#" class="addToDo" onsubmit="return addActiveList();">
      <input type="text" class="addToDoInfo" placeholder="Enter your to-do">
    </form>

투두 리스트는 아까 말한대로 Active리스트와 Done리스트로 나눌 것이기 때문에 각각 리스트의 형태를 잡아줍시다. 그리고 이 두 개의 리스트를 list라는 클래스로 묶어줍시다.

    <!--투두리스트-->
    <div class="list">
      <!--투두리스트Active-->
      <div class="activeToDo">
        <div class="activeTitle">Active</div>
        <ul type="none" class="listActive">
          <!--리스트에 아직 아무것도 넣지 않은 상태입니다.-->
        </ul>
      </div>

      <!--투두리스트Done-->
      <div class="doneToDo">
        <div class="doneTitle">Done</div>
        <ul type="none" class="listDone">
          <!--리스트에 아직 아무것도 넣지 않은 상태입니다.-->
        </ul>
      </div>
    </div>

이렇게 html 코드를 다 작성했습니다. 이제 css로 이쁘게 꾸며봅시다.

style.css

:root {
    --bg-color: #F1F3F8;
    --line-color:#D6E0F0;
    --font:'Raleway';
}

/*배경 및 body의 마진, 패딩 세팅*/
body {
    margin: 0;
    padding: 0;
    background-color: var(--bg-color);
    font-family: var(--font);
}

/*주 제목*/
.mainTitle {
    display: flex;
    justify-content: center;
    font-size: 48px;
    font-weight: 600;
    padding-top: 48px;
    padding-bottom: 72px;
    margin: 12px 10%;
    border-bottom: 1px solid var(--line-color);
}

/*투두 리스트 입력칸*/
.addToDo {
    display: flex;
    justify-content: center;
    margin-top: 36px;
    margin-bottom: 48px;
    width: 100%;
}
.addToDoInfo {
    width: 70%;
    line-height: normal;
    padding: 4px;
    height: 36px;
    font-size: 24px;
}
.addToDoInfo::placeholder {
    font-style: oblique;
    font-family: var(--font);
    color: var(--line-color);
    margin: 2px;
}

/*투두 리스트*/
.list {
    display: flex;
    justify-content: space-around;
    width: 100%;
    margin: 0 auto;
}
.list .activeToDo, .list .doneToDo {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 50%;
}
.list .activeTitle, .list .doneTitle {
    padding: 6px;
    border-bottom: 4px solid var(--line-color);
}
.list ul {
    padding:0;
    margin: 24px;
}
.list li {
    display: flex;
    justify-content: space-between;
    text-align: center;
    width: auto;
    margin:8px;
    padding: 6px;
    border-bottom: 2px solid var(--line-color);
}
.list button {
    border: none;
    background-color: var(--line-color);
    font-family: var(--font);
    padding: 4px 6px;
    margin-left: 12px;
}

기본적으로flex-box모델을 사용했습니다. :root에 자주 이용할 색깔을 정의해 줍니다. 저 같은 경우에는color hunt에서 색깔을 골라봤습니다. 그리고 폰트도 --font로 정의해둡니다.

:root {
    --bg-color: #F1F3F8;
    --line-color:#D6E0F0;
    --font:'Raleway';
}

body의 margin과 padding 값을 모두 0으로 해주고 배경색을 입혀줍니다. 그리고 사용할 폰트도 body 전체에 지정해줍니다.

/*배경 및 body의 마진, 패딩 세팅*/
body {
    margin: 0;
    padding: 0;
    background-color: var(--bg-color);
    font-family: var(--font);
}

주 제목이나 입력칸을 꾸미는 것은 솔직히 자신이 원하는대로 하시면 됩니다. 저는 약간의 포인트로 주 제목 밑에 선을 추가해 주

었고 입력칸 같은 경우에도 좀 더 크게 바꾸어 보았습니다. 참고해주세요.

/*주 제목*/
.mainTitle {
    display: flex;
    justify-content: center;
    font-size: 48px;
    font-weight: 600;
    padding-top: 48px;
    padding-bottom: 72px;
    margin: 12px 10%;
    border-bottom: 1px solid var(--line-color);
}

/*투두 리스트 입력칸*/
.addToDo {
    display: flex;
    justify-content: center;
    margin-top: 36px;
    margin-bottom: 48px;
    width: 100%;
}
.addToDoInfo {
    width: 70%;
    line-height: normal;
    padding: 4px;
    height: 36px;
    font-size: 24px;
}
.addToDoInfo::placeholder {
    font-style: oblique;
    font-family: var(--font);
    color: var(--line-color);
    margin: 2px;
}

리스트에 적용할 css도 포인트로 아래 밑줄을 살짝 넣어주었습니다. 근데 여기에서 libutton 에 대해서도 css를 적용한 걸 볼 수 있습니다. todo.js파일에서 알 수 있겠지만 이 두개는 입력칸에 해야 할 일을 적었을 때 libutton 태그를 생성해서 리스트에 넣어줄거라 미리 css를 작성해 놓은 것입니다. button 같은경우 새로 폰트를 지정해줘야되서 font-family: var(--font); 를 새로 지정해 주었습니다.

.list li {
    display: flex;
    justify-content: space-between;
    text-align: center;
    width: auto;
    margin:8px;
    padding: 6px;
    border-bottom: 2px solid var(--line-color);
}
.list button {
    border: none;
    background-color: var(--line-color);
    font-family: var(--font);
    padding: 4px 6px;
    margin-left: 12px;
}

이제 js파일을 살펴봅시다.

todo.js

 let i = j = 0;


function addActiveList() {
    let todovalue = document.querySelector(".addToDoInfo").value;
    let li = document.createElement("li");
    let button = document.createElement("button");
    button.className = "doneBtn" + i;
    li.innerHTML = todovalue;
    button.innerHTML = "Done";
    li.appendChild(button);
    document.querySelector(".listActive").appendChild(li);
    document.querySelector(".doneBtn"+i).addEventListener('click', doneActive);
    document.querySelector(".addToDoInfo").value = '';
    i++;
    return false;
}

function doneActive() {
    let content = this.parentNode;
    this.innerHTML = "delete";
    this.className = "deleteBtn" + j;
    document.querySelector(".deleteBtn"+j).addEventListener('click', deleteDone);
    j++;
    document.querySelector(".listDone").appendChild(content);
}

function deleteDone() {
    this.parentNode.parentNode.removeChild(this.parentNode);
}

함수는 총 3개로 이루어져 있습니다. Active리스트에 해야 할 일을 추가하는 addActiveList() , Active리스트에 있는 항목을 Done리스트로 옮길 doneActive() , Done리스트에 있는 항목을 아예 삭제할deleteDone() 이렇게 3개를 구현하면 됩니다.

addActiveList에서는 Active리스트에 새로운 항목을 추가하는 일을 합니다. 일단 어떤 형식으로 추가할까 고민해보다가 다음과 같이 넣으려고 합니다.

<li>해야할일<button class="doneBtn$">Done<button><li/>
 <!--여기서 button의 클래스의 '$'는 0이상의 정수를 이야기 합니다.-->

button 의 클래스는 Active리스트에 항목을 추가할 때 마다 클래스명을 달리하려고 합니다.
ex)

<!--첫번째 항목-->
<li>해야할일1<button class="doneBtn0">Done<button><li/>

<!--두번째 항목-->
<li>해야할일2<button class="doneBtn1">Done<button><li/>

<!--세번째 항목-->
<li>해야할일3<button class="doneBtn2">Done<button><li/>
 .
 .
 .

todo.js의 맨 윗줄에 있는 let i = j = 0; 은 이 기능을 위해 만들어 놓은 변수입니다. 자 이제 생성할때마다 클래스의 이름을 달리해야된다는 사실을 인지하고서 addActiveList()를 봅시다.

addActiveLsit

function addActiveList() {
    let todovalue = document.querySelector(".addToDoInfo").value;//.addToDoInfo라는 클래스를 가진 태그의 value값을 가지고 옵니다.
    let li = document.createElement("li");//li태그
    let button = document.createElement("button");//button태그
    button.className = "doneBtn" + i;//button태그에 들어갈 클래스명
    li.innerHTML = todovalue;//li에 todovalue를 html(텍스트)로 넣는다.
    button.innerHTML = "Done";//button에 "Done"이라는 텍스트를 넣는다.
    li.appendChild(button);//li에 button 붙이기, 항목이 완성되었다!
    document.querySelector(".listActive").appendChild(li);//리스트에 붙여서 완성된 항목 넣기
    document.querySelector(".doneBtn"+i).addEventListener('click', doneActive);//방금 넣은 항목에 대해 버튼을 클릭하면 함수 doneActive를 실행하게함.
    document.querySelector(".addToDoInfo").value = '';//input태그에 입력했던 것을 없애줌.
    i++;//다음 항목에 추가할 클래스명 맨 뒤에 붙는 숫자. 함수 한 번 마다 1씩 증가하게 해서 모든 항목의 버튼 클래스 명을 모두 다 다르게 한다.
    return false;
}

이제 doneActive() 를 살펴보겠습니다. 이 함수의 경우 Active리스트에 있던 항목의 Done 버튼을 누르면 이 항목을 Done리스트로 옮겨주는 역할을 합니다.

doneActive

    function doneActive() {
    let content = this.parentNode;
    //this는 함수가 실행된 바로 그 버튼을 말하므로(이 버튼의 경우 클래스명이 다르므로 그걸로 구분할 수 있다.) 이 버튼의 parentNode는 li태그가 되겠습니다.
    this.innerHTML = "delete";
    // this 안에 있는 html을 "delete"로 바꿔준다.
    this.className = "deleteBtn" + j;
    //여기도 마찬가지로 클래스명이 다 다른 버튼 구현을 위해 다음과 같은 형식을 활용했다.
    document.querySelector(".deleteBtn"+j).addEventListener('click', deleteDone);
    //버튼을 클릭하면 deleteDone함수가 실행되게 이벤트를 걸어준다. 이렇게 done리스트에 알맞은 항목으로 바뀌었다!
    j++;
    //버큰의 클래스명을 모두 다르게 하기 위해 j를 1씩 증가시킨다.
    document.querySelector(".listDone").appendChild(content);
    //바뀐 항목을 done리스트에 넣어줍시다.
}

마지막으로 deleteDone() 을 살펴보겠습니다. 이 함수는 Done리스트에 있는 항목의 Delete버튼을 누르면 아예 없애는 역할을 합니다.

deleteDone

function deleteDone() {
    this.parentNode.parentNode.removeChild(this.parentNode);
    //this의 부모노드의 부모노드인 ul태그 안에 있는 this의 부모 노드를 지웁니다. 즉 this의 li가 지워지게됩니다.
}

이렇게 todolist가 완성되었습니다! 기능이 많은 그런 투두 리스트는 아니지만 순수 자바스크립트만을 이용해서 해보려고 한 자그마한 프로젝트였고, 만족스럽습니다. 글, 코드에 대한 질문이나 피드백,개선 사항이 있다면 말씀해 주세요. 대환영입니다 ㅎㅎ.

'JS' 카테고리의 다른 글

[Vanilla/JavaScript]캘린더 만들기  (0) 2020.12.18
[Json/JavaScript]JSON 파싱 및 출력하는 법  (0) 2020.12.16