NodeList 와 HTMLCollection 에 대해 알아보아요.

Jhoon·2022년 12월 21일
0

참고서적: DOM - 잡았다, 요 돔!, DOM 을 깨우치다
DOM 을 보니 좀더 깊이 공부해야할것으로 느껴졌다. 아마 앞의 진도에서는 자바스크립트를 배우며 DOM 역시 같이 사용할 것이다. 더 깊숙히 공부하기 위해 해당 내용을 정리하며, 공부하도록 하자.
본 내용은 공부할 목적으로 적은 내용이므로, 틀린 내용이 포함될 수 있다.

ArrayLike Object


우리가 .list 라는 객체를 가져온다고 생각해보자.

<ul>
  <li class="list">item1</li>
  <li class="list">item2</li>
  <li class="list">item3</li>
</ul>
console.log(document.querySelectorAll(".list"));
// NodeList(3)[li.list, li.list, li.list]

이때, querySelectorAll 을 사용하여 li.list 를 가진 요소 전부를 가져온다.
콘솔로 찍힌 문구로 보면 NodeList 라고 되어있다.

그럼 이번에는 getElementsByClassName 으로 li.list 를 가져와본다.

console.log(document.getElementsByClassName(".list"));
// HTMLCollection(3)[li.list, li.list, li.list]

이번에는 HTMLCollection 의 문구가 콘솔상에 나온다.
여기서 궁금한것 NodeListHTMLCollection 이 무엇인지이다.

이를 이해하기 위해서는 ArrayLike 에 대해 알고 있는것이 좋다.
앞서서 DOMTree 에서 .list 를 가진 요소들을 가져왔다.
이때 해당 노드들의 배열(NodeListHTMLCollection)은 ArrayLike 객체이다.

ArrayLike 객체는 마치 Array 처럼 만들어진 객체이다.

다음을 보자.

const arrrayLike = {
	"0": "NodeElement1",
  	"1": "NodeElement2",
    "2": "NodeElement3",
    "length": 3,
};

// 이 arrayLike 는 마치 배열처럼 사용가능하다.
for (let i = 0; i < arrayLike.length; i += 1) {
	console.log(arrayLike[i]);
} // "NodeElement1" 
  // "NodeElement2" 
  // "NodeElement3"

하지만, 이 객체는 Obejct 이지 Array 가 아니다.
그러므로, Arraymethod 는 사용불가능하다.

즉, 요소를 담은 배열과 유사한 컬렉션 을 반환한다는 것이다.

그럼 왜 하나의 ArryLike 객체만 있으면 되지, 굳이 두개의 종류가 필요할까?

HTMLColletion 과 NodeLIst


HTMLCollection의 이름은 현대적 DOM의 이전, 구성요소로 HTML 요소만 지닐 수 있었던 시절에 정해졌습니다.

출처: MDN HTMLCollection

이 말은 HTMLCollection 은 구버전부터 존재했던 collection 이고,
NodeList 는 추후 DOM 이 발전하면서 생긴것같다.

둘의 차이점은 다음과 같다.

iterableliveproperty & method
HTMLCollectionooHTMLCollection.length
HTMLCollection.item()
HTMLCollection.namedItem()
NodeListoo and xNodeList.length
NodeList.entries()
NodeList.forEach()
NodeList.keys()
NodeList.values()

메서드에 대한 자세한건 아래의 링크에서 살펴보도록 하자.
MDN HTMLCollection
MDN NodeList

Table 만 보더라도 기능이 더 많이 생긴것을 볼 수 있다.
몰랐는데, 둘다 iterable 하다는것은 처음 알았네...

여기서 live 는 큰 차이점 중 하나이다.
liveDOM 에 값 반영시 해당 요소에 실시간으로 값이 반영되는 것이다.

HTMLCollection 으로 반환되는 listlive 하다.
반면, NodeList 는 특정 상황을 제외하고는, static 하다.

live 즉 객체의 요소들이 실시간으로 살아있다는것은 꽤나 골치아픈 문제를 발생시킨다.
다음을 보자.

<ul>
  <li class="list">item1</li>
  <li class="list">item2</li>
  <li class="list">item3</li>
</ul>
const $lists = document.getElementsByClassName("list");
// HTMLCollection(3)[li.list, li.list, li.list]

for (let i = 0; i < $lists.length; i += 1) {
	$lists[i].className = "list2";
}
console.log(document.body.innerHTML);
/*
  <ul>
    <li class="list2">item1</li>
    <li class="list">item2</li>
    <li class="list2">item3</li>
  </ul>
  <script src="index.js"></script>
*/

알수 없는 오류가 발생했다.

이러한 이유는, 실시간 반영(live) 때문이다.
처음 $lists[0]의 요소를 ClassName list2 로 변경할때 실시간 반영되어, $lists.length2 로 된다.

이때, $lists[0]item2 를 가진 li.list 가 되고
$lists[1]item3 을 가진 li.list 가 된다.

그러므로 i=1 이 된 상황에서 item3 을 가진 li.list 가 선택되고, className 을 변경한다.

결국은, item2 를 가진 liclassName 이 변경되지 않은채 존재하게 된다.

위처럼 예상치 못한 결과가 나올수 있으므로, 반복문 사용시 배열로 변경해서 사용하는것이 좋을 것 같다.

반면, staticNodeList 는 전혀다른 결과를 가져온다.
실시간 반영이 아니라 현재 문서에 대한 snapshot 이기 때문에, 위처럼 작동하지 않는다.

const $lists = document.querySelectorAll(".list");
// HTMLCollection(3)[li.list, li.list, li.list]

for (let i = 0; i < $lists.length; i += 1) {
	$lists[i].className = "list2";
}
console.log(document.body.innerHTML);
/*
  <ul>
    <li class="list2">item1</li>
    <li class="list2">item2</li>
    <li class="list2">item3</li>
  </ul>
  <script src="index.js"></script>
*/

위 상황은 예상가능한 동작을 한다.
NodeListHTMLCollection 을 배열로 변경하고 싶다면,
Array.from() 을 사용하면된다.

반면, NodeLIst 역시 livecollection 을 반환할 수 있다.
Node.childNodes 가 그렇다.

해당 요소를 반복 순회하는 일이 많다고 싶으면, 그냥 Array 객체로 만든이후 처리하는것이 현명할 듯싶다.

이렇게 오늘은 NodeListHTMLCollection 의 차이를 알아보았다.
위 차이를 알아야 추후 개발시 발생할 수 있는 문제점중 하나로 기억할 수 있게 된것 같다

profile
익숙해지면 다 할수 있어!!

0개의 댓글