
문제 상황
의도한 동작 & 코드 & 결과
원인 찾기
원인
해결 방법
추가 내용
정리
회고
이전에는 모든 요소를 삭제하는 경우에 querySelectorAll 로 골라서 삭제하거나 아예 새로운 List를 만들어서 교체하는 방식으로 구현을 했었다. 이번에는 DOM을 학습하면서 한 번도 안써본 Element.Children, Node.removeChild(), Element.remove() 를 사용하는 과정에서 문제가 발생했다.
한 일 모두 지우기 버튼 클릭시 checked된 아이템을 전부 화면에서 제거하기const list = document.querySelector('.list');
const btn = document.querySelector('button');
const items = list.children; // HTMLCollection
btn.addEventListener('click', removeCheckedItems);
function removeCheckedItems() {
for (const item of items) {
item.firstChild.checked && list.removeChild(item);
}
}
<body>
<ul id="world" class="list">
<li class="item item0"><input type="checkbox" checked />할 일 0</li>
<li class="item item1"><input type="checkbox" checked />할 일 1</li>
<li class="item item2"><input type="checkbox" checked />할 일 2</li>
<li class="item item3"><input type="checkbox" checked />할 일 3</li>
<li class="item item4"><input type="checkbox" checked />할 일 4</li>
<li class="item item5"><input type="checkbox" checked />할 일 5</li>
</ul>
<button type="button">한 일 모두 지우기</button>
</body>
items를 for...of로 순회하면서 해당 input이 checked이면 list에서 해당 item을 제거도록 작성했다.list.children은 콘솔에 찍어봤을 때 배열의 형태를 보여서 배열을 반환한다고 짐작했다. querySelectorAll()도 배열을 반환한다고 생각(forEach 메소드를 갖고 있어서)하고 있었어서 둘 다 배열을 반환한다고 생각했다. 그리고 결정적으로 for...of를 사용할 수 있어서 더 배열이라고 믿었다...HTMLCollection, Static NodeList을 리턴한다.
for-of문은 3번 반복됐다.
HTMLCollection는 배열이 아니다.Element.children가 반환한 HTMLCollection가 배열이 아닐 수 있겠다는 생각이 들었다.items를 isArray()로 확인해보니 false가 나왔다.function removeCheckedItems() {
items.forEach(item => {
item.firstChild.checked && list.removeChild(item);
});
}
```js
Array.isArray(items) // false
```
HTMLCollection 은?HTMLCollection 인터페이스는 요소의 문서 내 순서대로 정렬된 일반 컬렉션(arguments처럼 배열과 유사한 객체)을 나타내며 리스트에서 선택할 때 필요한 메서드와 속성을 제공합니다.
HTML DOM 내의 HTMLCollection은 문서가 바뀔 때 실시간으로 업데이트됩니다.https://developer.mozilla.org/ko/docs/Web/API/HTMLCollection
HTMLCollection 은 배열이 아니고 순서대로 정렬된 배열과 유사한 객체이다.length, 메서드로는 인덱스의 값을 반환하는 item()이 있는데 for...of 를 쓸 수 있게 억지로 만든 느낌?? 이다.DOM 컬렉션 객체다. 유사배열객체이자 이터러블이다.HTMLCollection은 실시간으로 업데이트된다. DOM에 변경이 생기면 자동으로 업데이트가 되서 MDN에서는 adding, moving, or removing nodes를 할 때 복사본을 만들어서 하는 걸 추천한다.HTMLCollection 의 Live한 속성 때문에 발생했다.
(index 0, 1, 2) 난 이후 index 3의 값이 없기 때문에 종료됐다.const list = document.querySelector('.list');
const btn = document.querySelector('button');
const items = list.children; // HTMLCollection
btn.addEventListener('click', removeCheckedItems);
// forEach
function removeCheckedItems1() {
const arrayItems = Array.from(items);
arrayItems.forEach(item => {
item.firstChild.checked && list.removeChild(item);
});
}
// for...of
function removeCheckedItems2() {
const arrayItems = Array.from(items);
for (const item of arrayItems) {
item.firstChild.checked && list.removeChild(item);
}
}
Array.from 으로 배열로 변환 후에 forEach, for...of 를 사용했다.
Element.children 대신 querySelectorAll() 를 사용한다.const list = document.querySelector('.list');
const btn = document.querySelector('button');
const items = list.querySelectorAll('.item'); // NodeList
btn.addEventListener('click', removeCheckedItems);
function removeCheckedItems() {
items.forEach(item => {
if (item.firstChild.checked) list.removeChild(item);
});
}
querySelectorAll()은 Static NodeList 를 반환하는데 이것도 HTMLCollection와 같은 유사배열객체이지만 Live 속성이 없다.NodeList는 내부 요소들을 순회할 수 있는 forEach 메소드를 가진다.querySelectorAll()은 Static NodeList 를 반환한다.https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
https://developer.mozilla.org/en-US/docs/Web/API/NodeList
NodeList는 HTMLCollection과 같이 DOM 컬렉션 객체다. 유사배열 객체이면서 이터러블이다.NodeList는 Live 와 Static 두 가지가 있다. Live는 위에서 본 HTMLCollection처럼 실시간 업데이트가 된다. Node.childNodes가 Live NodeList를 반환한다.querySelectorAll()는 Static NodeList를 반환하는데 이름 그대로 정적이라서 어떠한 변경도 원본 리스트에 영향을 주지 못한다. 그래서 이번에 발생한 문제가 생기지 않는다.HTMLCollection 보다 다양한 메소드를 갖는다. item(), entries(), forEach(), keys(), values()HTMLCollection NodeList는 DOM 컬렉션 객체이며 유사배열객체이고 이터러블이다.HTMLCollectionLive(살아있는) 객체이다.NodeListquerySelectorAll()로 만들어졌을 경우에 Static childNodes 프로퍼티로 반환한 경우 Live배열로 변환해 사용하면 된다.NodeList는 forEach 메소드가 있지만, 배열로 변환하면 다양한 고차함수까지도 안전하게 사용할 수 있다.제대로 아는 게 중요하는 걸 깨닫는다.
아직 경험이 적지만 현재 내 수준에서 겪는 문제들은 거의 정확하게 알지 못해서 발생하는 것 같다. 이번에도 단순히 콘솔에서 [] 으로 감싸져 있다고 배열이라고 짐작했다. mdn에 검색해보기만 하면 나오는 것인데... 하지만 이제 제대로 안다는 것이 기쁘고 문제를 해결한 것이 뿌듯하다.
https://developer.mozilla.org/en-US/docs/Web/API/NodeList
https://developer.mozilla.org/ko/docs/Web/API/HTMLCollection
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll