지난 시간에 이어 이번에는 JavaScript 코드 작성에 대한 이야기로
포스팅을 이어보려고 한다.
작성된 코드에 대한 설명이 주된 내용이므로,
관심이 있거나 필요한 부분만 골라서 보면 될 것 같다.
(혹시나 포스팅 내용에 오류가 있다면 제발 꼭 좀 알려줬으면 좋겠다..)
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>To Do List</title>
<meta name="description" content="바닐라 JS로 만드는 To Do List 웹 애플리케이션" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="index.css">
</head>
<body>
<header>
<h1>To Do List</h1>
</header>
<section>
<form class="input-to-do">
<input type="text" placeholder="할 일을 입력해주세요!" />
<button type="submit">입력</button>
</form>
<article>
<h2>What I Have To Do?</h2>
<div>
<ul class="what-to-do">
</ul>
</div>
</article>
</section>
<footer>
<div>COPYRIGHT 2022. JAENY, All rights reserved</div>
</footer>
<script src="./ToDoList.js"></script>
</body>
</html>
가장 집중한 부분은 semantic 태그를 최대한 잘 사용해보자 하는 것이었고,
기본적으로 필요한 태그나 내용을 최대한 빠뜨리지 말자는 것이었다.
아래 내용을 펼쳐서 확인하면, 각 태그 사용에 대한 의도를 간략하게 서술하였다.
<meta charset="utf-8" />
<title>
<meta name="description" content="...">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="index.css">
<header>
<h1>
<section>
<form>
<input type="text" placeholder="할 일을 입력해주세요!">
<button type="submit">
<article>
<h2>
article 내부 <div>
<ul>
<footer>
footer 내부 <div>
<script src="./ToDoList.js">
/**
* QuerySelect for input Form eventListener
* @type {Element}
*/
const FormToDo = document.querySelector(".input-to-do");
/**
* QuerySelect for input eventListener
* @type {Element}
*/
const InputToDo = FormToDo.querySelector("input");
/**
* QuerySelect for ul to paint Element eventListener
* @type {Element}
*/
const WhatToDo = document.querySelector(".what-to-do");
/**
* sessionStorage data keyName
* @type {string}
*/
const ToDoList = "toDoList";
/**
* sessionStorage data valueName
* @type {Array}
*/
let toDoList = [];
필요한 변수를 const 혹은 재할당 가능한 let 키워드 통해 선언해주었다.
아래 내용을 펼쳐서 확인하면, 각 변수에 대한 의도를 간략하게 서술하였다.
동작을 위한 기능을 선언하기 위해 화살표 함수 문법을 사용하여, 각 기능별 함수를 선언 및 할당하였다.
차례대로 내용과 의도를 서술하도록 하겠다. (접힌 글을 펼쳐서 확인!)
/**
* create 함수: input의 이벤트처리
* @param {Event} event submit 이벤트
*/
const createToDo = (event) => {
event.preventDefault();
const toDo = InputToDo.value;
paintToDo(toDo);
setToDo(toDo);
InputToDo.value = "";
};
2-1. create(생성) 기능을 위한 함수
form을 통해 input value가 submit 되었을 때,
새로고침이 되면서 이벤트 발생이 무시되는 것을 막기 위해
preventDefault 함수를 먼저 실행시켜주었다
이후 상수 toDo를 선언하여 value값을 받고,
화면을 실질적으로 그려주는 함수인 paintToDo에 toDo를 인자로 넘겨서
화면에 value 값을 통해 출력을 시켜주고
sessionStorage에 데이터를 저장해주는 setToDo 함수에 toDo를 넘겨주어서
submit을 함으로 sessionStorage에 해당 데이터가 남아있도록 처리해준다.
이 후 input의 값을 빈 값으로 만들어서 초기화를 시켜준다.
/**
* paint 함수: 실질적으로 화면에 그리기
* @param {String} toDo input에 넣은 text(할 일)
*/
const paintToDo = (toDo) => {
const li = document.createElement("li");
const input = document.createElement("input");
input.setAttribute("type", "checkbox");
input.addEventListener("click", checkToDo)
const label = document.createElement("label");
label.innerHTML = toDo;
const button = document.createElement("button");
button.addEventListener("click", deleteToDo)
button.innerHTML = "삭제";
li.appendChild(input);
li.appendChild(label);
li.appendChild(button);
li.id = toDoList.length + 1;
WhatToDo.appendChild(li);
}
2-2. paint(화면출력) 기능을 위한 함수
toDo 즉 input value 값을 인자로 받아 동작하는 함수로서
HTML 문서의 ul 태그 내부에 li, input, label, button 태그를 통해 화면 출력 기능을 담당한다
먼저 li 태그를 생성하는 함수를 li 상수에 할당해주고,
checkbox를 생성하기 위해 input 태그를 생성하는 함수를 input에 할당해준다.
이 후 input 태그의 type 속성을 checkbox로 지정하기 위한 setAttribute 함수를 실행해주고,
checkbox의 체크여부에 따라 EventListener를 활용하기 위해 addEventListener를 통해
click에 대한 Event를 checkToDo 함수에 할당해준다
이 후 input value를 목록으로 그려줄 label 태그를 생성하는 함수를 label 상수에 할당하고,
label의 내용을 input value값으로 채워주기 위해 innerHTML을 통해 input value인
toDo를 label의 내용으로 넣어주도록 한다.
마지막으로 delete 기능을 위한 button 태그를 생성하는 함수를 button 상수에 할당하고,
button의 클릭을 통해 delete 기능을 동작하기 위해 addEventListener를 통해
click에 대한 Event를 deleteToDo 함수에 할당해준다
그리고 button의 기능을 확실히 명시하기 위해
button의 내용을 innerHTML 함수를 통해 "삭제" 라고 명시해주었다.
이후 해당 내용들을 담기 위해 각 태그들을 li 태그 내부의 child로 넣어주기 위해
appendChild 함수를 사용해 각 태그들을 child로 지정하여 넣어주고
li 태그의 삭제 기능 활용을 위해 id값을 toDoList.length를 기준으로 추가해준다.
해당하는 li 태그를 최종적으로 ul 태그인 WhatToDo 상수의 child로 생성하도록 하여
함수의 기능을 선언하도록 했다.
/**
* list 내에 checkbox 이벤트 처리:
* 할일 처리상태 => label text-decoration
* @param {Event} event 체크박스 클릭이벤트
*/
const checkToDo = (event) => {
const label = event.target.nextSibling;
if (event.target.checked) {
label.setAttribute("class", "checked-label")
} else {
label.setAttribute("class", "unchecked-label")
}
}
2-3. checkToDo(할 일 수행여부 체크) 기능을 위한 함수
WhatToDo(ul 태그) 내부에 있는 li 태그의 자식 요소 중, input type="checkbox" 인
체크박스의 체크 여부에 따라 DOM 조작을 통해 label 태그의 스타일링을 변환해주는 함수이다
click 이벤트 객체를 인자로 받아 해당 타겟의 바로 옆인 label 태그를 지정하여 상수에 할당하고,
체크 여부(event.target.checked가 true 혹은 false인 조건)에 따라 class 속성을 부여함으로
스타일링에 변화를 주도록 함수를 선언하였다.
/**
* list 내에 button을 통한 이벤트 처리:
* parentNode를 이용해 list 삭제 + sessionStorage 최신화
* @param {Event} event 삭제버튼 클릭이벤트
*/
const deleteToDo = (event) => {
event.preventDefault();
const li = event.target.parentNode;
WhatToDo.removeChild(li);
toDoList = toDoList.filter((toDo) => toDo.id !== Number(li.id));
sessionStorage.setItem(ToDoList, JSON.stringify(toDoList));
}
2-4. deleteToDo(할 일 목록 삭제) 기능을 위한 함수
WhatToDo(ul 태그) 내부에 있는 li 태그의 자식 요소 중 button을 클릭했을 때
DOM 조작을 통해 li 전체를 삭제하고 sessionStorage를 최신화 처리해주는 함수이다
먼저 DOM 조작을 위해 click Event.target의 부모인 li 태그 전체를 상수로 할당하고,
해당 태그만 삭제될 수 있도록 WhatToDo 내에서 선언된 요소만 removeChild 함수를 통해 삭제처리 한다
이후 filter 함수를 통해 sessionStorage의 value인 toDoList의 id값과 li의 id값을 비교하여
해당 값을 제외한 나머지 값으로 데이터를 최신화 하여준다. 이후 sessionStorage에 해당 데이터를
다시 setItem을 통해 ToDoList의 value값으로 다시 할당하여 준다.
할당할때는 JSON 문자열이 필요하므로 해당 배열을 JSON.stringfy 함수를 통해 변환하여 할당해준다.
/**
* sessionStorage에 데이터가 있을 경우
* 초기화면에 그려주는 역할
*/
const getToDo = () => {
const loaded = sessionStorage.getItem(ToDoList);
if (loaded !== null) {
const parsed = JSON.parse(loaded);
for (let toDo of parsed) {
const {text} = toDo;
paintToDo(text);
setToDo(text);
}
}
}
2-5. getToDo(sessionStorage에 데이터가 있을때, 초기화면으로 그려주는 역할) 기능을 위한 함수
웹 페이지 접속시, 기존 접속이력이 유지된 상태일 경우(단순히 새로고침을 한 경우)에
기존에 입력되어 있던 데이터가 있다면 해당 데이터를 다시 불러와서 그려주는 함수이다
먼저 sessionStorage에서 ToDoList 데이터를 받아오기 위해 getItem 함수를 활용하여,
데이터를 상수 loaded에 할당하여주고 해당 데이터의 존재여부(data가 null인지 아닌지)에 따라
해당 내용을 그려줄 수 있도록 조건에 대한 분기를 설정하고, 만약 데이터가 있다면(null이 아니라면)
parsed 상수에 loaded 내의 데이터를 JavaScript 객체로 변환하여 해당 데이터 내의 text 값을
paintToDo 함수를 통해 화면에 그려주고, setToDo 함수를 통해 sessionStorage에 데이터를 반영하도록 한다
/**
* sessionStorage에 데이터를 추가하여
* 새로고침시에도 데이터가 남아있게 해줌
* @param {String} toDo input에 넣은 text(할 일)
*/
const setToDo = (toDo) => {
/**
* sessionStorage에 넣을 value 정제
* @type {{text: String, id: Number}}
*/
const obj = {
text: toDo,
id: toDoList.length + 1,
}
toDoList.push(obj);
sessionStorage.setItem(ToDoList, JSON.stringify(toDoList));
}
2-6. setToDo(sessionStorage에 데이터를 반영) 기능을 위한 함수
sessionStorage의 활용을 위해, input value인 toDo를 인자로 받아 sessionStorage에
해당 데이터를 저장해주는 역할을 하는 함수이다
toDo 즉, input value를 인자로 받아 id와 text를 가진 객체인 obj에 데이터를 할당한다.
여기서 obj의 값은 id와 text로 이루어져 있는데, id는 toDoList의 length를 기준으로 하고,
text는 input value 값을 넣어주도록 한다
그리고 sessionStorage에 넘겨주기 위해 빈 배열에 해당 객체를 할당(추가)하고,
sessionStorage에 ToDoList라는 key 값을 통해 value로 JSON 문자열로 변환된 toDoList를 넘겨준다
/**
* 초기화면 및 form event 처리
*/
const initToDo = () => {
getToDo();
FormToDo.addEventListener("submit", createToDo);
}
initToDo();
2-7. initToDo(초기화면 구성 및 form event 추가) 기능을 위한 함수
웹 페이지 접속 시, sessionStorage의 데이터 유무에 따라 화면을 그려주고
form의 submit에 대한 Event를 createToDo 함수에 할당해주는 함수이다.
해당 함수는 script 호출시 바로 실행이 될 수 있도록
해당 스크립트 파일 내에서 실행을 바로 시켜준다.
@font-face {
font-family: 'EarlyFontDiary';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_220508@1.0/EarlyFontDiary.woff2') format('woff2');
font-weight: normal;
font-style: normal;
}
html {
font-family: 'EarlyFontDiary';
}
body {
margin: 0;
}
header {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
h1 {
color: rgb(255, 104, 104);
font-size: 40px;
font-weight: bold;
}
}
section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.input-to-do {
margin-bottom: 10px;
input {
padding: 5px;
font-size: 16px;
font-family: 'EarlyFontDiary';
}
button {
padding: 5px;
font-size: 16px;
font-family: 'EarlyFontDiary';
}
}
article {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: 15px;
div{
width: 100%;
height: 100%;
min-width: 120px;
min-height: 60px;
background: rgb(255, 122, 122);
border-radius: 10px;
padding: 20px;
@media screen and (max-width:768px) {
max-width: 320px;
}
.what-to-do {
width: 100%;
padding: 0;
margin: 0;
list-style-type: none;
:last-child {
margin-bottom: 0;
}
@media screen and (max-width:768px) {
max-width: 320px;
}
li {
display: flex;
justify-content: space-between;
align-items: center;
background: rgb(255, 193, 193);
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
@media screen and (max-width:768px) {
max-width: 280px;
}
input {
width: 25px;
}
label {
min-width: 180px;
margin: 0 10px;
text-align: center;
@media screen and (max-width:768px) {
max-width: 180px;
max-height: 18px;
overflow: scroll;
}
}
.checked-label {
min-width: 180px;
margin: 0 10px;
text-align: center;
color: #7b7b7b;
text-decoration: line-through;
@media screen and (max-width:768px) {
max-width: 180px;
max-height: 18px;
overflow: scroll;
}
}
button {
width: 45px;
font-family: 'EarlyFontDiary';
}
}
}
}
}
}
footer {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
div {
background: rgba($color: #ffffff, $alpha: 0.5);
font-size: 12px;
padding: 5px;
border-radius: 5px;
}
}
해당 코드를 통해 아래와 같은 화면 출력을 확인할 수 있다.
이번 포스팅은 좀 역대급으로 지치는 포스팅이었던 것 같다.
아직 설명에 대해서는 부족한게 많아서 하나하나 설명하려다 보니
좀 더 에너지를 많이 쏟은 것 같다.
체력이 남는다면 다음 포스팅에서는, JSDoc을 조금 다뤄보도록 하겠다!
[Web API] stopPropagation으로 이벤트 버블링을 방지해보자! (0) | 2022.11.03 |
---|---|
[개인 프로젝트] Vanilla JS로 To Do List 만들기 (1) (0) | 2022.08.28 |
[Restart 프론트엔드 스터디] #1. 자바스크립트의 역사 (출처: poiemaweb) (0) | 2022.08.15 |
댓글 영역