들어가며
신촌IT연합동아리 CEOS에서 프론트엔드 스터디를 하면서 진행한 미션을 되돌아본다. 미션 주제는 Vanilla JS로 To-do list를 구현하는 것이었다. 할 일과 한 일을 구분하여 보여주고 클릭 시 반대편으로 이동하는 기능이 있다. 또 외부 폰트와 localStorage를 이용하는 것이 미션의 내용이었다.
자세한 미션의 내용은 여기서 볼 수 있다.
결과를 github Pages로 배포했는데 여기서 체험할 수 있다.
github 주소
https://github.com/BonJunKu/vanilla-todo-14th
index.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vanilla Todo</title>
<link rel="stylesheet" href="style.css" />
<script
src="https://kit.fontawesome.com/2fdda89094.js"
crossorigin="anonymous"
></script>
<script defer type="module" src="script.mjs"></script>
</head>
<!-- body = waiting part + done part + add part -->
<body>
<main class="container">
<!-- waiting part -->
<section class="waiting__container">
<img class="lion" src="./img/lion.png" alt="lion" />
<div class="waiting__title">
<h2>✍진행 중인 작업(<span class="waiting__count">0</span>)</h2>
</div>
<div class="waiting__content">
<ul class="waiting__list"></ul>
</div>
</section>
<!-- done part -->
<section class="done__container">
<div class="done__title">
<h2>😆완료된 작업(<span class="done__count">0</span>)</h2>
</div>
<div class="done__content">
<ul class="done__list"></ul>
</div>
</section>
<!-- add part -->
<section class="add__container">
<input
class="add__input"
type="text"
style="text-align: center"
placeholder="새로운 할 일을 추가해주세요!"
onfocus="this.placeholder=``; this.style.textAlign=`left`"
onblur="this.placeholder=`새로운 할 일을 추가해주세요!`; this.style.textAlign=`center`"
/>
<button class="add__button">+</button>
</section>
</main>
</body>
</html>
```
Style.css
```css
/* Global */
:root {
/* Color */
--color-dark-orange: #ac5300;
--color-white: #ffffff;
--color-pink: #fdc7ff;
--color-waiting: #fff5bf;
--color-done: #fff1a0;
--color-add: #fff5bf;
/* Font size */
--font-medium: 18px;
--font-small: 16px;
/* Size */
--size-border-radius: 15px;
}
/* Universal tags */
* {
box-sizing: border-box;
font-family: 'ELAND_Choice_M';
}
/* default settings */
html,
body {
margin: 0;
}
input,
button,
ul,
p {
all: unset;
}
ul {
list-style: none;
}
body {
width: 100vw;
height: 100vh;
/* Center Container */
display: flex;
align-items: center;
justify-content: center;
/* Coloring */
background-color: var(--color-dark-orange);
}
/* Typography */
@font-face {
font-family: 'ELAND_Choice_M';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts-20-12@1.0/ELAND_Choice_M.woff')
format('woff');
font-weight: normal;
font-style: normal;
}
h2 {
font-size: var(--font-medium);
}
.container {
width: 350px;
height: 600px;
display: flex;
flex-direction: column;
align-items: stretch;
box-shadow: 0 0 25px rgba(0, 0, 0, 0.25);
}
/* waiting part */
.waiting__container {
background-color: var(--color-waiting);
height: 275px;
position: relative;
border-top-left-radius: var(--size-border-radius);
border-top-right-radius: var(--size-border-radius);
}
.waiting__title {
margin: 0 15px;
}
.waiting__content {
overflow-y: scroll;
height: 80%;
}
span.waiting__list__item {
padding: 0 15px;
}
.waiting__list__item .deleteButton {
margin: 0 10px;
display: none;
}
.lion {
opacity: 0;
width: 50%;
top: 150px;
left: 75px;
position: absolute;
transition: all 0.5s ease-out;
}
/* done part */
.done__container {
background-color: var(--color-done);
height: 275px;
}
.done__title {
margin: 0 15px;
}
.done__content {
overflow-y: scroll;
height: 80%;
}
.done__list__item {
margin: 0 15px;
}
.done__list__item .deleteButton {
margin: 0 10px;
}
/* add part */
.add__container {
background-color: var(--color-add);
height: 50px;
display: flex;
flex-direction: row;
justify-content: space-between;
border-bottom-left-radius: var(--size-border-radius);
border-bottom-right-radius: var(--size-border-radius);
}
.add__container input {
margin: 10px 0 10px 10px;
padding: 0 10px;
background-color: white;
border: solid 1px rgba(0, 0, 0, 0.25);
border-radius: var(--size-border-radius);
width: 270px;
}
.add__container button {
margin: 10px 10px 10px 0;
background-color: var(--color-dark-orange);
width: 30px;
height: 30px;
text-align: center;
border-radius: 50%;
color: white;
}
```
script.mjs
```JavaScript
;
localStorage.removeItem(String(id));
render();
}
//make deleteButton visible
function appearDeleteButton() {
const button = this.lastChild;
button.style.display = 'inline';
button.style.opacity = 0;
button.style.transition = 'all 1s ease';
setTimeout(() => {
button.style.opacity = 1;
this.style.color = 'brown';
this.style.fontWeight = '900';
});
}
//make deleteButton invisible
function disappearDeleteButton() {
const button = this.lastChild;
button.style.display = 'none';
this.style.color = 'black';
this.style.fontWeight = '400';
}
//when everything is done, LION comes out!
function summonLion() {
lion.style.display = 'block';
setTimeout(() => {
lion.style.opacity = 1;
lion.style.top = '70px';
});
}
function vanishLion() {
lion.style.opacity = 0;
lion.style.display = 'none';
lion.style.top = '100px';
}
```
피드백 받은 부분
칭찬받은 점 👍
- 로컬스토리지에 저장을 하는 과정에서 데이터 순서가 바뀌면 안되기 때문에 인덱스를 같이 저장을 해주었는데, 이 덕분에 삭제를 할 때 O(1)로 삭제 구현을 할 수 있었다.
```JavaScript
function deleteItem() {
let id = this.parentNode.firstChild.getAttribute('id');
itemList.splice(id, 1);
localStorage.removeItem(String(id));
render();
}
```
functional programming
을 최대한 사용한 점이 좋았다고 한다.
개선할 점 😅
- 인라인 스타일링은 지양하는 것이 좋다고 한다.
- prettier를 적용했는데, 적용이 안되었다(…) 알아보니 VS Code의 default editor관련 문제였다.
==
연산자 대신===
연산자를 더 쓰자!!- 죄다 addEventListner로 처리를 했는데, 그것보다는 css에서 처리할 수 있던 점이 많았던 것 같다…