Jihye's records
[DOM] Shopping List js (complete)
[DOM] Shopping List 완성 - js 추가
Date: May 2, 2021
HTML markup
Full Code: JS
'use strict';
const LIST_KEY = 'Shopping-List';
const list_ul = document.querySelector('.listBox > ul');
const form = document.querySelector('.input');
const input = document.querySelector('input');
const addBtn = document.querySelector('.add');
let list = [];
// localStorage용 arr
let cnt = 0;
function addElement(itemName) {
const span = document.createElement('span'); // span 생성
const i = document.createElement('i'); // i 생성
const li = document.createElement('li'); // li 생성
li.id = cnt;
i.setAttribute('class', 'fas fa-trash-alt delete'); // i에 class 지정
span.textContent = itemName; // item 이름 넣기
li.append(span, i); // li에 span, i 넣기. appendChild는 두 개 이상 못 넣음
list_ul.append(li); // ul에 li 넣기
list.push({ item: itemName, idx: cnt++ }); // listArr에 넣기
i.addEventListener('click', delItem);
setStorage();
}
function setStorage() {
localStorage.setItem(LIST_KEY, JSON.stringify(list));
}
function addItem(event) {
const itemName = input.value;
if (itemName !== '') {
addElement(itemName);
}
input.value = ''; // input의 값 없애기
input.focus();
// localStorage의 arr 세팅하기
}
function showItem() {
const items = JSON.parse(localStorage.getItem(LIST_KEY));
if (items !== null) {
items.forEach((item) => {
addElement(item['item']);
});
}
}
function delItem(event) {
const removeLi = event.target.parentNode; // li 할당
removeLi.remove(); // li 제거
const newItems = list.filter((item) => {
return item.idx !== parseInt(removeLi.id);
});
list = newItems;
setStorage();
}
form.addEventListener('submit', (event) => {
event.preventDefault();
addItem(event);
});
addBtn.addEventListener('click', addItem);
showItem();
- localStorage 사용해서 F5(refresh) 후에도 list가 사라지지 않도록 함.
자세한 동작 설명
-
전역
const LIST_KEY = 'Shopping-List'; const list_ul = document.querySelector('.listBox > ul'); // <li> 생성 후 넣기 위함 const form = document.querySelector('.input'); // form의 submit 처리 위함 const input = document.querySelector('input'); // input 태그 값 받아오기 위함 const addBtn = document.querySelector('.add'); // 추가 버튼 구현 위함 let list = []; // localStorage 덮어쓰기할 임시 arr let cnt = 0; // idx 지정하기
-
input -submit 및 addBtn 동작하게 만들기
form.addEventListener('submit', (event) => { event.preventDefault(); // submit 발생 시 자동 새로고침 방지 addItem(event); }); addBtn.addEventListener('click', addItem);
-
addItem 함수
function addItem(event) { const itemName = input.value; if (itemName !== '') { addElement(itemName); } input.value = ''; // input의 값 없애기 input.focus(); }
-
addElement 함수 // document에 element 추가하기
function addElement(itemName) { const span = document.createElement('span'); // span 생성 const i = document.createElement('i'); // i 생성 const li = document.createElement('li'); // li 생성 li.id = cnt; i.setAttribute('class', 'fas fa-trash-alt delete'); // i에 class 지정 span.textContent = itemName; // item 이름 넣기 li.append(span, i); // li에 span, i 넣기. appendChild는 두 개 이상 못 넣음 list_ul.append(li); // ul에 li 넣기 list.push({ item: itemName, idx: cnt++ }); // listArr에 넣기 i.addEventListener('click', delItem); setStorage(); }
-
span, i, li를 만들어서 속성을 지정해준다.
-
li에는 id를 cnt값으로 넣어서 localStorage와 동시에 삭제가 용이하도록 설정한다.
-
list라는 빈 배열에 item 이름과 idx 를 함께 지정하여 obj형태로 push(뒤로 쌓기)한다.
=== 초기에 localStorage에 있는 값을 무조건 다 훑기 때문에 전역에서는 빈 list였으나 이 함수가 실행되고 난 후의 list는 무조건 localStorage의 값을 다 갖고 있다. 그래서 push하면 값이 계속 쌓이게 된다!
-
i에 click 이벤트 핸들러를 등록한다. delItem 함수를 할당한 뒤 setStorage(바뀐 list를 localStorage에 덮어씌우기)를 호출한다.
-
-
setStorage 함수
function setStorage() { localStorage.setItem(LIST_KEY, JSON.stringify(list)); }
- localStorage에는 obj 형식을 저장할 수 없기 때문에 (string 만 가능) JSON.stringify를 이용하여 string 화 시켜준다.
-
showItem 함수
function showItem() { const items = JSON.parse(localStorage.getItem(LIST_KEY)); if (items !== null) { items.forEach((item) => { addElement(item['item']); }); } }
- localStorage에 넣은 obj 배열을 JSON.parse를 통해 변환하여 받아온다.
- items를 하나씩 돌면서 key가 ‘item’ 인 value들을 가져와 addElement에 전달한다.
- addElement에서 list에 push해주는 역할을 하기 때문에 list는 항상 localStorage의 온전한 형태를 갖고 있다.
- 초기에 한 번 무조건 실행해준다. document에 element 추가하여 사용자에게 보여야 하기 때문.
-
delItem 함수
function delItem(event) { const removeLi = event.target.parentNode; // li 할당 removeLi.remove(); // li 제거 const newItems = list.filter((item) => { return item.idx !== parseInt(removeLi.id); }); list = newItems; setStorage(); }
-
removeLi 변수에는 지금 event로 전달된 의 부모 (<li>)를 할당한다. (parentNode 사용)
-
remove API를 이용하여 해당 <li>를 삭제한다. -> document에서 사라짐!
-
현재 list에 있는 obj들의 각 idx와 지금 삭제된 <li>에 할당되어있던 id들을 비교하여 같지 않은 것들만 담는다.
ex) 나는 현재 id값이 2인 <li>를 삭제하였다. 이를 localStorage에도 적용시키기 위해 filter API를 사용한다. localStorage에는 idx값이 2인 obj가 들어 있는데, 이를 포함한 모든 obj들의 idx를 비교하면 이미 제거한 id와 모두 비교하면 결국에 newItems에는 삭제된 id: 2 (idx: 2) 빼고 모든 obj들이 담기게 된다. 이를 list에 덮어씌우고 localStorage에 저장한다.
-
Thinking
- localStorage를 사용하는 것은 예전에 momentum clone 에서도 했었는데, 좀 오래돼서 기억이 안났다.
- 그래서 코드를 참고했다. (toDo.js) notion: momentum clone
- localStorage를 사용하기 위해 빈 list를 사용하여 이 함수 저 함수에서 돌아가면서 list의 값을 변경하고 덮어씌우고 하는 방식이 갑자기 이해가 안돼서 애를 먹었는데, 이렇게 다시 정리하면서 써보니까 이해가 된다 ㅜㅜ 다행..