JavaScript로 투두리스트 만들기

gu·2022년 12월 19일
0

토이프로젝트

목록 보기
6/6

⭐ UI

💻 Stack

Html, Css, JavaScript

💻 기능

  1. 사용자가 할일을 입력한다.
  2. 입력 후 추가 혹은 엔터를 누르면 할일이 추가 된다.
  3. 할일에 체크박스를 누르면 할일이 끝났다고 간주하여 밑줄이 그어진다.
  4. 휴지통 아이콘을 누르면 할일이 리스트에서 삭제된다.
  5. 탭에서 할일전체목록과 진행중, 할일끝난 아이템들을 분류하여 볼수있다.

💻 코드

⚡ index.html

<body>
    <div class="container">
        <header class="top">
            <h1>나의 투두리스트</h1>
            <p id="date">날짜</p>
        </header>
        <div id="input-area">
            <input id="task-input" type="text" placeholder="할일을 입력해주세요">
            <button id="add-button">추가</button>
        </div>
        <section class="task-area">
            <div class="task-tabs">
                <div id="under-line"></div>
                <div id="all">All</div>
                <div id="ongoing">Not Done</div>
                <div id="done">Done</div>
            </div>
            <div id="task-board">
                <!-- <div class="task">
                    <div>go home</div>
                    <div>
                        <button>Check</button>
                        <button>Delete</button>
                    </div>
                </div> -->
            </div>
        </section>
    </div>

    <script src="https://kit.fontawesome.com/20ba346cfc.js" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.0/moment.min.js"></script>
    <script src="./app.js"></script>
</body>

자바스크립트파일에서 쓰일 요소들은 id값을 줬다. 그리고 주석처리해놓은 .task부분은 html구조를 처음에 짤때 미리 짜놓고 자바스크립트에서 innerHtml로 넣은 구조들이다.

⚡ style.css

@import url('https://fonts.googleapis.com/css2?family=Nanum+Gothic&display=swap');


*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

button{
    border: none;
    background-color: none;
    background-color: white;
    cursor: pointer;
}

body{
    background: url(/images/jellyfish-698521_1920.jpg) no-repeat;
    min-height: 100vh;
    width: 100vw;
    display: flex;
    flex-direction: column;
    font-family: 'Nanum Gothic', sans-serif;
    color: #424242;
    align-items: center;
    justify-content: center;
}

.container{
    width: 900px;
    /* border: 8px solid rgb(0, 73, 176); */
    border-radius: 8px;
    height: 600px;
    padding: 20px 10px;
    margin-top: 20px;
    background-color: white;
}

.top{
    display: flex;
    justify-content: space-between;
    margin-bottom: 15px;
}

#input-area{
    margin-bottom: 15px;
}
#task-input{
    border: none;
    background-color: rgba(211, 255, 255, 0.723);
    padding: 3px 5px;
    height: 30px;
    width: 50%;
}
#add-button{
    height: 28px;
    border: none;
    background-color: skyblue;
    color: white;
    padding: 0px 10px;
    cursor: pointer;
}
.task-area{
    border: 2px solid skyblue;
    background-color: #fff;
}

.task-tabs{
    display: flex;
    border-bottom: 1px solid skyblue;
    position: relative;
    font-weight: 500;
}
.task-tabs div{
    padding: 1em;
    color: #424242;
}
.task-tabs div:hover{
    color: #757575;
}
.task-tabs div:active{
    color: #263238;
}
#under-line{
    height: 4px;
    background-color: rgb(67, 208, 255);
    position: absolute;
    width: 60px;
    left: 0;
    top: 52px;
    border-radius: 4px;
    padding: 0;
    transition: 0.5s;
}


.task{
    display: flex;
    justify-content: space-between;
    padding: 1em;
}
.task-done{
    text-decoration: line-through;
}

.task-tabs div{
    cursor: pointer;
}

.task-board{
    background-color: white;
}

.fa-regular{
    font-size: 20px;
    margin-right: 5px;
    color: rgb(78, 196, 242);
}
.fa-regular:hover{
    color: blue;
}

.fa-solid{
    font-size: 20px;
    color: rgb(78, 196, 242);
}
.fa-solid:hover{
    color: blue;
}

투두리스트 ui를 꾸며줄 간단한 스타일코드를 작성했다.

⚡ app.js

이번 투두리스트에서 꼭 필요한 기능은 할일추가하기, 추가된 할일 렌더링하기, 삭제 및 할일끝남 표시 추가하기, 탭한 메뉴에 맞는 리스트 보여주기까지 크게 4가지로 꼽을 수 있다. 하나하나 살펴보자.

let taskInput = document.getElementById('task-input');
let addButton = document.getElementById('add-button');
let taskList=[];
let mode = "all";
let filterList=[];
let underLine = document.getElementById("under-line");
let tabs =document.querySelectorAll(".task-tabs div");

전역에서 사용할 변수선언

💥 할일 추가하기

<script>
// task 공백방지
addButton.addEventListener("click",(e)=>{
    if(taskInput.value.length==0){
        alert("할일을 입력해주세요.");
    }else{addTask()}
});

function addTask(){
    let task = {
        id: randomIDGenerate(), //할일 아이템당 유니크한 id값!
        taskContent: taskInput.value,
        isComplete:false,
    }

    taskList.push(task);
    render(); //할일을 화면에 목록형식으로 보여주는 함수 미리 적어둠!
    console.log("todo",task)
}

// 리스트마다 랜덤id주기
function randomIDGenerate(){
    return (performance.now().toString(36)+Math.random().toString(36)).replace(/\./g,"");
    // 어떤 함수의 결과물이 다른곳에서 쓰일때 리턴이 꼭 필요!!
}
</script>

사용자가 인풋란에 할일을 적고 추가버튼을 눌렀을때 할일이 추가되는 함수를 먼저 만들어보았다. addTask라는 함수를 만들어 task라는 객체를 선언했다. 이안에는 할일 아이템 하나 당 들어가야할 키와 그 값들이 있다. 먼저 유니크한 id값이 있어야한다. 왜하냐면 한 아이템(할일)마다 차별성있는 값이 있어야 선택한 아이템을 삭제 혹은 체크를 할 수 있기 때문이다. 나는 구글링해서 찾기 함수를 넣어줬다. 그리고 내용이 필요한데, 이것은 사용자가 입력할 인풋란의 값을 가져와 넣어주었다. 해당되는 값을 가져오는 .value함수를 이용했다. 그리고 할일이 끝난 것과 해야할 일을 탭창에서 분류해야하기 때문에 isComplete키를 만들어 초기값을 false로 설정했다.(할일끝난 것을 투두리스트에 추가하지는 않으므로 !)
전역 변수 중 taskList라는 배열값을 가진 게 있었다. 나는 여기에 투두리스트 아이템을 보관할 것이다. 그래서 task의 객체를 taskList배열안에 푸쉬해줬다. 그럼 일단 할일이 taskList에 잘담기게된다. 이제 이 할일을 화면에 보여주는 render함수를 만들어야 한다.

💥 할일 아이템을 화면에서 보여주기

<script>
function render(){
    
    let resultHTML = '';
    for(let i=0;i<tasklist.length;i++){
        resultHTML += `<div class="task">
        <div>${tasklist[i].taskContent}</div>
        <div class="btn">
            <buttontoken interpolation">${tasklist[i].id}')"><i class="fa-regular fa-square"></i></button>
            <buttontoken interpolation">${tasklist[i].id}')"><i class="fa-solid fa-trash"></i></button>
        </div>
        </div>`
       }
    taskInput.value=''
}
    document.getElementById("task-board").innerHTML = resultHTML;
}
</script>

사용자가 할일을 추가할때마다 렌더링이 되어야한다. 그러기위해 for문을 이용해서 아이템을 추가하는 형식으로 만들었다. resultHTML이라는 변수를 만들었고 여기에 들어갈 아이템 구조를 html형식으로 넣어줬다. 이 내용값을 형식 그대로 전달하기 위해 할일 목록을 감싸는 #task-board에 innerHTML로 넣어줬다.

❗ innerHTML과 innerText 차이
(내가 헷갈린 기억이 있어서 적으면서 다시 공부해보겠다)
저 두개는 비슷한듯 조금의 차이가 있다.

  • innerHTML
    'Element'의 속성으로, element내에 포함 된 Html 또는 XML 마크업을 가져오거나 태그와 함께 입력하여 내용을 직접 설정할 수 있다.즉, innerHTML을 사용하면 내부 HTML 코드를 JavaScript 코드에서 새 내용으로 변경할 수 있다.
  • innerText
    역시 'Element'의 속성으로, element내에서 사용자에게 보여지는 text값들을 가져오거나 설정할 수 있다. text값만 다루기에 html태그가 불가능하다.

💥 삭제 및 할일끝남 표시 추가하기

할일을 완료했으면 체크박스를 클릭함과 동시에 할일이 적힌 텍스트에 밑줄이 그어지면서 완료 표시를 내고 체크박스 옆 휴지통아이템을 클릭하면 아이템을 목록에서 삭제해주는 함수를 만들어 보겠다.

<script>
function toggleComplete(id){
    // taskList[i].id
    console.log("체크인",id)
    for(let i=0;i<taskList.length;i++){
        if(taskList[i].id===id){
            taskList[i].isComplete=!taskList[i].isComplete; 
            break;
        }
    }
    render(); // 랜더 함수를 호출해야 화면에서도 나타난다.
    console.log("체크리스트",taskList)
}

function deleteTask(id){
    console.log("삭제",id)
    for(let i=0;i<taskList.length;i++){
        if(taskList[i].id===id){
            taskList.splice(i,1)
            break;
        }
    }
    render();
}

</script>

addTask함수에서 만들어준 아이템의 유니크한 id값을 받아오고 반복문을 이용해 해당 아이템의 id값과 클릭한 버튼의 id값이 같으면 isComplete에 반대되는 값을 넣어준다. true값으로 바꾸지 않고 반대되는값을 넣어주는 이유는 할일을 표시했다가 다시 할일 진행으로 번갈아가며 바꿀수있기 때문이다. 그리고 알맞게 값을 바꾸어줬으면 반복문은 종료된다. (break;)
비슷한 형식으로 삭제하는 함수도 만들었다. 같은 조건에서 리턴코드만 수정하면되는데, 리스트에서 해당되는 아이템을 삭제해야하므로 배열함수 .splice()를 사용했다. 간단하게 설명하면 배열 i번째에있는 아이템 1개를 삭제한다는 거다. 이 역시 조건에맞는 결과를 리턴후 함수는 종료된다.

💥 탭메뉴에 따라 할일 분류하기

<script>
for(let i=1;i<tabs.length;i++){
    tabs[i].addEventListener("click",(e)=>{filter(e)})
}
function filter(e){
   
    filterList=[]
    // console.log("탭",e.target.id)
    if(e){
        mode = e.target.id
    }
    if(mode=="all"){
        render();
    }else if(mode=="ongoing"){
        for(let i=0;i<taskList.length;i++){
            if(taskList[i].isComplete==false){
                filterList.push(taskList[i])
            }
        }
        render();
    }else if(mode=="done"){
        for(let i=0;i<taskList.length;i++){
            if(taskList[i].isComplete==true){
                filterList.push(taskList[i])
            }
        }
        render();
    }
}
</script>

먼저 전역 변수 mode는 초기값이 all이다. 탭을 누르기전에 할일 목록전체를 화면에 띄워야하기 때문이다. 그리고 탭을 누르면 이벤트가 작용되어 filter함수가 호출된다.
필터함수에는 탭메뉴인 전체항목, 할일진행중인 항목, 끝난 항목으로 아이템이 분류되어야 하기때문에 if문을 사용하여 탭마다 다른 조건을 걸어주고 filterList에 푸쉬해주고 화면에 보이도록 render()도 마지막마다 호출해주었다.

❗ 주의! 필터함수를 적용하기위해 addTask함수를 조금 수정해줘야한다.
나는 addTask에 list라는 배열값이 들어갈 객체를 만들어 if문을 통해 mode가 all이면 lisㅅ에 taskList(필터링되지않은 전체 할일목록)을 넣어주고 onging이나 done이면 filterList를 넣어줬다.

⭐ 마무리

이 프로젝트도 복습겸 다시 만들어보고 블로그에 작성하는건데 역시 도움이 된다. 이전에 처음에 이프로젝트를 진행할때는 filter부분에서 작은 오류가 있었다. 탭메뉴 all에서만 삭제 및 수정이 되고 다른 탭메뉴에서는 기능이 먹히지 않았다. 당시에는 자바스크립트를 공부한지 얼마안되어서 끙끙 머리싸매다가 결국 포기하고 미뤄놨던 것을 몇개월이 지난 현재시점에서 다시 프로젝트를 만들어보면서 10분만에 해결했다. 해결 또한 간단했던게 콘솔창 에러 메시지를 제대로 확인안해서 이전에는 해결을 못했던 것같다. 이번에는 에러메시지를 정확히 확인하고 코드를 수정하면서 원하는 기능을 다 구현해낸 토이 프로젝트를 완성할수있었다. ✌️

⚡ 도메인 및 깃주소

깃주소
도메인

0개의 댓글