이번 리액트 수업하기 전, 간단하게 자바스크립트로 Todo List를 실습해보는 것이다.
나는 사실 자바스크립트로는 로그인, 회원가입을 제외한 기능을 구현해본 적이 없다. 즉 게시물이라던지 화면에서 삭제하고, 그런걸 제대로 해본 적이 없다. 그래서 이번 실습을 할 때, 코드는 이해가 되었으나 뭔가 바로 구현을 하기에는 좀 어려움이 있었다.
일단 순서대로 진행을 해보겠다. 아래 기본적인 화면이다.
할일을 적는 input과, 추가 버튼, 그리고 리스트와 삭제 버튼이 있다. 차근차근 진행해보겠다.
일단 html 코드이다.
<body>
<div id="todo">
<h1>Todo List - 목록 조회 :)</h1>
<p>파일 경로: <span id="filepath"></span></p>
</header>
<div id="main">
<div id="container">
<ul>
<li>
<h2>쇼핑 목록</h2>
<div class="todoinput">
<input type="text" autofocus">
<button type="button">추가</button>
</div>
<ul class="todolist">
<li>
<span>1</span>
<span><s>샘플1</s></span>
<button type="button">삭제</button>
</li>
<li>
<span>2</span>
<span>샘플2</span>
<button type="button">삭제</button>
</li>
<li>
<span>3</span>
<span>샘플3</span>
<button type="button">삭제</button>
</li>
</ul>
</li>
</ul>
</div>
</div>
<script type="text/javascript">
document.querySelector('#filepath').textContent = `ch${document.URL.split('/ch')[1]}index.html`;
// 샘플 목록
let itemList = [
{ no: 1, title: '두부', done: true },
{ no: 2, title: '계란', done: false },
{ no: 3, title: '라면', done: true },
];
위는 기본적인 리스트이다.
1. 목록 출력
여기에 이제 ul 요소를 선택해서 변수에 저장하여 ul노트를 통해 조작을 해보자.
const todoListElem = document.querySelector('.todolist');
일단 화면에 나와있듯이 기존에 있던 리스트를 화면에서 삭제해보자
// 기존 목록 삭제
while (todoListElem.firstChild) {
todoListElem.firstChild.remove();
}
while 반복문을 통해 첫 번째 요소가 모두 삭제 될 때까지, 반복한다. ul 안에 li 몇개가 있던 삭제를 한다.
삭제를 했으니, 리스트를 추가해야겠지. 근데 여기서 추가하는 방법이 여러가지가 있지만,
대표적으로 2가지를 뽑아서 진행하겠다.
첫 번째는 innerHTML을 이용하여 동적으로 생성하는 방법과 두 번째 방법은 요소를 DOM으로 선택하여 진행하는 방법이 있다.
첫 번째 방법은 간단하다.
아래와 같이 동적으로 생성을 한 후, 만약 추가가 된다면
function getTodoItemElem(item) {
// return (`
// <li>
// <span>${item.no}</span>
// <span>${item.done ? `<s>${item.title}</s>` : item.title}</span>
// <button type="button" onclick="deleteItem()">삭제</button>
// </li>
아래와 배열을 돌면서 생성하는 것이다.
itemList.forEach((item, index) => {
const liElem = getTodoItemElem(item);
todoListElem.innerHTML += liElem;
})
근데 첫 번째 방법이 아닌 두 번재 방법으로 구현해보자, 첫 번째 방법은 많이 사용했으며, 이는 이벤트 처리하기가 좀 어려워진다.
itemList.forEach((item, index) => {
const liElem = getTodoItemElem(item);
todoListElem.appendChild(liElem);
})
노가다 이지만 각 요소를 이런 식으로 할당을 해주었다.
// element Node
// <li>
const liElem = document.createElement('li');
// <span>
const noElem = document.createElement('span');
// <span>
const titleElem = document.createElement('span');
// <button>
const deleteElem = document.createElement('button');
// 1
const noTxt = document.createTextNode(item.no);
// 샘플1
const titleTxt = document.createTextNode(item.title);
// 삭제
const deleteTxt = document.createTextNode('삭제');
deleteElem.appendChild(deleteTxt);
각 li, span, button 그리고 숫자에 들어갈 데아터, 타이틀, 삭제를 변수에 할당해준다.
그리고 아래와 같이 각각의 줄에 노드를 추가하여 생성하는 것이다.
// <span>1</span>
noElem.appendChild(noTxt);
// <span><s>샘플1</s></span>
if (item.done) { // 완료
// <s>
const sElem = document.createElement('s');
sElem.appendChild(titleTxt);
// <span><s>샘플1</s></span>
titleElem.appendChild(sElem);
} else { // 미완료
// <span>샘플1</span>
titleElem.appendChild(titleTxt);
}
// <button type="button">
deleteElem.setAttribute('type', 'button');
// <button type="button">삭제</button>
deleteElem.appendChild(deleteTxt);
// <li><span>1</span></li>
liElem.appendChild(noElem);
// <li>
// <span>1</span>
// <span><s>샘플1</s></span>
// </li>
liElem.appendChild(titleElem);
// <li>
// <span>1</span>
// <span><s>샘플1</s></span>
// <button type="button">삭제</button>
// </li>
liElem.appendChild(deleteElem);
// <li data-no="1">
// <span>1</span>
// <span>샘플1</span>
// <button type="button">삭제</button>
// </li>
liElem.setAttribute('data-no', item.no);
// <li data-no="1">
// <span>1</span>
// <span onclick="toggleDone(1)">샘플1</span>
// <button type="button">삭제</button>
// </li>
titleElem.setAttribute('onclick', `toggleDone(${item.no})`);
// <li data-no="1">
// <span>1</span>
// <span onclick="toggleDone(1)">샘플1</span>
// <button type="button" onclick="deleteItem(1)">삭제</button>
// </li>
deleteElem.setAttribute('onclick', `deleteItem(${item.no})`);
return liElem;
2. 등록
추가 버튼에 onclick으로 이벤트 함수를 추가해준다.
아래 코드와 같이 이벤트 함수에 todoinput안에 있는 input 요소를 선택하여, 이 값이 빈값이 아니면 아이템을 추가해주고, 그리고 input은 빈값으로 출력한다.
그리고 다시 포커스 할 수 있도록 한다. 여기에 엔터 이벤트를 추가해서 엔터를 입력했을 때, 이벤트 함수가 작동 될 수 있도록 만든 것이다.
// '추가' 클릭 이벤트 핸들러
const handleAdd = () => {
const inputElem = document.querySelector('.todoinput > input');
if (inputElem.value.trim() !== '') {
addItem(inputElem.value);
inputElem.value = '';
inputElem.focus();
}
};
// 엔터 이벤트
const handleAddKeyup = (event) => {
if (event.key === 'Enter') handleAdd();
};
아래 코드는 이제 투두를 추가시켜주는 로직이다.
중간에 번호 부분에서
no: itemList[itemList.length - 1].no + 1 이 부분은 itemList[itemList.length - 1]는 배열의 마지막을 뜻하며, 이를 통해 마지막 요소를 가져올 수 있다. 그리고 .no + 1 이 부분은 마지막 값에 1을 더하면서 이전 번호보다 1을 더하여 배열을 추가시키는 것이다.
그래서 push 메서드는 통해 데이터 갱신을 한다.
그리고 맨 아래 코드이 경우 이제 화면에 출력하는 것이다.
// 할일 추가
function addItem(title) {
const item = {
no: itemList[itemList.length - 1].no + 1,
title,
done: false,
};
// TODO: 데이터 갱신
// 맨 끝 아이템 추가
itemList.push(item);
// TODO: 화면 갱신
const liElem = getTodoItemElem(item);
const todoListElem = document.querySelector('.todolist');
todoListElem.appendChild(liElem);
3. 완료 / 미완료 처리
위와 같이 완료가 되서 클릭하면 밑줄이 되고, 그게 아니면 저런식으로 되는 건데
이는 토글 형식으로 하면 된다.
function toggleDone(no) {
// TODO: 데이터 갱신
let selectedItem = itemList.find(item => item.no === no);
selectedItem.done = !selectedItem.done;
위 코드와 같이 find 메서드를 통해, item의 숫자가 no 숫자랑 같다면 그것만 뽑아주는 것이다. 이 항목만 검사를 반환하고 다른 항목은 검사하지 않는다.
결국 번호를 찾아서 클릭 시 !selectedItem.done으로 false로 바꿔주고, 또 클릭 하면 true로 바꿔주는 것이다.
그리고 이것은 이제 화면을 갱신 하는건데, 아래 와 같이 요소를 노드 생성해주고, selectedItem.done이 true인지, false로 밑줄 생성과 아닌 것을 나눈 것이다.
// TODO: 화면 갱신
const selectedLiElem = document.querySelector(`.todolist > li[data-no="${no}"]`);
// <li>
// <span>2</span>
// <span>샘플2</span>
// <button type="button">삭제</button>
// </li>
const titleSpanElem = selectedLiElem.children[1];
if (selectedItem.done) {
// <span>샘플2</span> -> <span><s><샘플2</s></span>
const sElem = document.createElement('s');
//<s>샘플2</s>
sElem.appendChild(titleSpanElem.firstChild);
titleSpanElem.appendChild(sElem);
} else {
// <span><s><샘플2</s></span> -> <span>샘플2</span>
titleSpanElem.appendChild(titleSpanElem.firstChild.firstChild);
titleSpanElem.firstChild.remove();
}
}
4. 할 일 삭제
이것은 이제 삭제 버튼을 누르면 삭제를 시키는 것인데, 저 filter를 통해서 item.no 와 no의 숫자가 같다면 제외하고, 그 나머지를 새로운 배열이 생성이 된다. 즉 내가 클릭한 항목만 비교를 하여 삭제를 시키는 것이다.
function deleteItem(no) {
// TODO: 데이터 갱신
itemList = itemList.filter(item => item.no !== no);
// TODO: 화면 갱신
const selectedLiElem = document.querySelector(`.todolist > li[data-no="${no}"]`);
selectedLiElem.remove();
}
그리고 화면 갱신은 이제 번호를 실질적으로 삭제를 시키는 것이다. 화면에서 삭제되는 것을 출력 시키는 코드이다.
위에 할 일 삭제의 경우는 다른 방법이 있다. 그 방법에 대해서는 추후 다시 업로드를 하겠다.
'JavaScript' 카테고리의 다른 글
[vanilla practice] Counter - 2 (0) | 2024.11.08 |
---|---|
[vanilla practice] Counter 실습 (0) | 2024.11.07 |
key 저장 하는 법 (0) | 2024.10.25 |
[모던자바스크립트 Deep Dive] 47장. 에러 처리 (2) | 2024.10.10 |
[모던자바스크립트 Deep Dive] 44장. REST API (0) | 2024.10.10 |