2일에 걸쳐 JavaScript를 배운 뒤 어떤 프로그램을 만들지 고민하다가 간단한 투두리스트를 구현해보기로 했다. 복습할 겸 HTML과 CSS부분도 처음부터 다 만들었기에 디자인이 예쁘지는 않지만 다 만들고 나니 뿌듯하다(삽질도 많이 했다).
다음에는 캘린더, 타임스탬프 등의 기능이 들어간 다이어리 앱을 개발해보고 싶다✨
👉🏻 완성된 투두리스트 페이지
구현하고 싶은 기능들은 다음과 같다.
<!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
'use strict';
let itemList = [];
let inputButton = document.querySelector(".input__button");
inputButton.addEventListener("click", addItem);
strict mode
로 작성하였고 우선 할 일들을 담을 배열을 선언해주었다. querySelector()
를 사용하여 HTML에서 input__button
클래스를 가져와 inputButton
변수에 할당해주었다. var 대신 es6부터 적용된 let 구문을 사용하여 변수를 선언해주었다.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();
}
querySelector()
를 이용하여 받아온 뒤 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번 기능
을 구현하기 위해 showList()
함수를 만들었다.
1번 기능
입력창에 할 일을 입력하고 ➕버튼을 누르면 리스트에 할 일을 표시
먼저 for문을 통해 배열에 들어온 요소를 ul
의 형태로 list 변수에 담았고, innerHTML
속성을 사용하여 item__list
클래스에 list를 담았다.
innerHTML은 element에 포함된 HTML 또는 XML 마크업을 가져오거나 설정한다.
출처: https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
querySelectorAll()
을 활용해 클래스가 close인 모든 element를 가져와 deleteButtons에 저장한 뒤, deleteButtons 배열의 각 요소를 인덱싱해 x버튼이 클릭될 경우 해당 item이 삭제되도록 설정했다.
2번 기능
할 일 오른쪽에 있는 ✖️버튼을 누르면 리스트에서 할 일 삭제하기
splice
함수를 사용하면 지정한 인덱스(id)부터 지정한 숫자(1)만큼 요소를 삭제할 수 있다.
let checkList = document.querySelector('.item__list');
checkList.addEventListener('click', event => {
if (event.target.tagName === 'LI') {
event.target.classList.toggle('checked');
}
});
3번 기능
구현을 위해 클래스가 item__list
인 요소를 가져와 checkList 변수에 저장했다.click
이벤트가 발생했을 때 checked
클래스가 존재한다면 제거하고, 존재하지 않으면 추가하도록 설정했다.
3번 기능
리스트에 표시된 할 일을 클릭하면 ✔️버튼이 왼쪽에 나타나면서할 일에 줄 긋기
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! 웹 프로그래밍을 위한 자바스크립트 기본편 (고경희 지음)
깔끔하고 쉬운 설명감사해요! todolist만드는데 큰 도움이 되었습니다 :-)!!