TODO-LIST 만들기

최종 결과

 

🎂 과제 목표

1) HTML로 기본 구조를 만든다.

2) 자바스크립트로 할 일 추가 기능을 구현한다. (입력 필드, 버튼)

 

🎂 추가로 시도한 것

1) 할 일 완료 표시, 체크박스 꾸미기

2) 할 일 삭제 기능

3) 배경, 버튼 그라데이션 색상 적용

4) 입력창이 빈 상태일 경우 alert 띄우기

 

 

구현 방법

🥐 입력을 받아 할 일 목록에 추가하기 + 입력창이 비었을 때 alert + 삭제

<div class="userInput">
    <input type="text" id="taskInput" placeholder="할 일을 입력하세요!">
    <button type="submit" id="taskAddButton">추가</button>
</div>
<div class="listBody">
    <ul id="list">
    </ul>
</div>

'할 일(task)'들은 같은 분류의 요소들이 모여있는 목록이기에 ul로 만들기로 했고, 사용자가 입력한 요소들을 li로 추가하도록 구성했다.

 

// task 추가
const taskAddButton = document.querySelector('#taskAddButton');
taskAddButton.addEventListener("click", addTask);

const taskList = document.querySelector('#list');
const taskInput = document.querySelector('#taskInput');
function addTask() {
    let newTask = taskInput.value;
    if(newTask == "") {
        alert("할 일을 입력하세요!");
        return;
    }

    let newLi = document.createElement('li');
    newLi.className = "task";
    newLi.innerHTML = `
        <input type="checkbox" name="task" id="task${taskList.children.length + 1}">
        <label for="task${taskList.children.length + 1}">
        <span class="checkboxIcon"></span>
        <span class="taskContent">${newTask}</span>
        </label>
    `;

    let deleteButton = document.createElement('button');
    deleteButton.className = "taskDeleteButton";
    deleteButton.addEventListener("click", deleteTask);
    
    newLi.appendChild(deleteButton);
    taskList.appendChild(newLi);

    taskInput.value = '';
}

// task 삭제
function deleteTask(e) {
    e.target.parentElement.remove();
}

querySelector로 버튼, 할 일 목록, 입력창을 받아왔다.

버튼을 클릭했을 때 addTask()가 실행되면서 입력이 빈문자열일 경우 alert 메세지를 띄우고 반환한다.

li 요소를 만들어서 원하는 구조의 html을 추가해주었고 이 때 생성되는 버튼을 받아 이벤트를 설정하기 위해 button element도 따로 생성하였다.

삭제버튼을 누르면 부모요소인 li를 삭제한다.

 

처음엔 삭제버튼이 생성되기도 전에 addEventListener가 실행되는 구조여서 오류가 생겼었다. 특정 객체를 조작할 때 그 객체가 어떤 상태인지도 잘 고려해봐야겠다고 생각했다.

 

🥐 체크박스 꾸미기 + 할 일 완료

li.task > input[type="checkbox"] {
  display: none;
}

input[type="checkbox"] + label {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.checkboxIcon {
  margin-right: 10px;
  width: 15px;
  height: 15px;
  background-color: transparent;
  border: 1px solid black;

  position: relative;
}

input[type="checkbox"]:checked + label .checkboxIcon::after {
    content: "✔";
    text-align: center;
    font-size: 1.5rem;

    position: absolute;
    top: -125%;
}

input[type="checkbox"]:checked + label .taskContent {
  text-decoration: line-through;
}

기본 체크박스를 보이지 않게 설정하고, label 내에 체크박스 아이콘으로 사용할 요소를 만든다.

체크박스가 체크된 상태일 때 체크박스 아이콘 요소에 가상 요소를 생성하여 내가 원하는 체크 기호를 넣어주었다.

체크박스가 체크된 상태일 때 글자가 담긴 taskContent에는 가로지르는 줄을 그어줬다.

 

🥐 배경, 버튼에 그라데이션 색상 적용

.userInput > button {
  width: 10%;

  border: none;
  background-image: linear-gradient(to right bottom, #5575ff, #e064ff);
  color: white;
}

linear-gradient()를 사용하여 적용했다.

linear-gradient는 image의 일종인 gradient를 반환하기 때문에 background-color가 아닌 background-image 등에 적용해줘야 한다!

 

 

전체 코드

🍰 HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Todo-List</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="mainContainer">
        <div class="title">
            <h1>TODO</h1>
        </div>
        <div class="userInput">
            <input type="text" id="taskInput" placeholder="할 일을 입력하세요!">
            <button type="submit" id="taskAddButton">추가</button>
        </div>
        <div class="listBody">
            <ul id="list">
            </ul>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

 

🍰 CSS

@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100..900&display=swap");

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

body {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;

  background-image: linear-gradient(
    to right bottom,
    #b298ff,
    rgb(201, 241, 255)
  );
}

.mainContainer {
  border-radius: 20px;
  background-color: white;

  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;

  height: 60%;
  width: 30%;
}

.title {
  flex: 1;

  display: flex;
  align-items: end;
}

.title > h1 {
  font-family: "Noto Sans KR", sans-serif;
  font-optical-sizing: auto;
  font-weight: 400;
  font-style: normal;
}

.userInput {
  flex: 1;
  width: 100%;

  display: flex;
  justify-content: center;
  align-items: center;
}

.userInput > * {
  border-radius: 5px;
  height: 2.8rem;

  font-family: "Noto Sans KR", sans-serif;
  font-optical-sizing: auto;
  font-weight: 400;
  font-style: normal;
  font-size: 0.8rem;
}

.userInput > input {
  width: 70%;
  border: 1px solid lightgrey;
  margin-right: 0.5rem;
  padding-left: 1rem;
}

.userInput > button {
  width: 10%;

  border: none;
  background-image: linear-gradient(to right bottom, #5575ff, #e064ff);
  color: white;
}

.listBody {
  flex: 7;
  overflow: auto;

  width: 80%;
}

ul.list {
  list-style: none;
}

li.task {
  height: 2.5rem;
  margin-top: 0.5rem;

  display: flex;
  align-items: center;

  border-bottom: 1px solid lightgrey;

  font-family: "Noto Sans KR", sans-serif;
  font-optical-sizing: auto;
  font-weight: 300;
  font-style: normal;
}

li.task > input[type="checkbox"] {
  display: none;
}

input[type="checkbox"] + label {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.checkboxIcon {
  margin-right: 10px;
  width: 15px;
  height: 15px;
  background-color: transparent;
  border: 1px solid black;

  position: relative;
}

input[type="checkbox"]:checked + label .checkboxIcon::after {
    content: "✔";
    text-align: center;
    font-size: 1.5rem;

    position: absolute;
    top: -125%;
}

input[type="checkbox"]:checked + label .taskContent {
  text-decoration: line-through;
}

.taskDeleteButton {
  background: url("asset/can.png") no-repeat center;
  width: 1.5rem;
  height: 1.5rem;
  margin: 0 10px 0 auto;
  border: none;
}

button {
  cursor: pointer;
}

 

🍰 JavaScript

// task 추가
const taskAddButton = document.querySelector('#taskAddButton');
taskAddButton.addEventListener("click", addTask);

const taskList = document.querySelector('#list');
const taskInput = document.querySelector('#taskInput');
function addTask() {
    let newTask = taskInput.value;
    if(newTask == "") {
        alert("할 일을 입력하세요!");
        return;
    }

    let newLi = document.createElement('li');
    newLi.className = "task";
    newLi.innerHTML = `
        <input type="checkbox" name="task" id="task${taskList.children.length + 1}">
        <label for="task${taskList.children.length + 1}">
        <span class="checkboxIcon"></span>
        <span class="taskContent">${newTask}</span>
        </label>
    `;

    let deleteButton = document.createElement('button');
    deleteButton.className = "taskDeleteButton";
    deleteButton.addEventListener("click", deleteTask);
    
    newLi.appendChild(deleteButton);
    taskList.appendChild(newLi);

    taskInput.value = '';
}

// task 삭제
function deleteTask(e) {
    e.target.parentElement.remove();
}

'프로젝트 > 개인 과제' 카테고리의 다른 글

한식메뉴 렌더링하기  (0) 2024.06.26
[Javascript] 숫자기억게임 만들기  (0) 2024.06.20