참고서적: DOM - 잡았다, 요 돔!, DOM 을 깨우치다
DOM 을 보니 좀더 깊이 공부해야할것으로 느껴졌다. 아마 앞의 진도에서는 자바스크립트를 배우며 DOM 역시 같이 사용할 것이다. 더 깊숙히 공부하기 위해 해당 내용을 정리하며, 공부하도록 하자.
본 내용은 공부할 목적으로 적은 내용이므로, 틀린 내용이 포함될 수 있다.
우리가 .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
의 문구가 콘솔상에 나온다.
여기서 궁금한것 NodeList
와 HTMLCollection
이 무엇인지이다.
이를 이해하기 위해서는 ArrayLike
에 대해 알고 있는것이 좋다.
앞서서 DOMTree
에서 .list
를 가진 요소들을 가져왔다.
이때 해당 노드들의 배열(NodeList
및 HTMLCollection
)은 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
가 아니다.
그러므로, Array
의 method
는 사용불가능하다.
즉, 요소를 담은 배열과 유사한 컬렉션
을 반환한다는 것이다.
그럼 왜 하나의 ArryLike
객체만 있으면 되지, 굳이 두개의 종류가 필요할까?
HTMLCollection
의 이름은 현대적DOM
의 이전, 구성요소로 HTML 요소만 지닐 수 있었던 시절에 정해졌습니다.
이 말은 HTMLCollection
은 구버전부터 존재했던 collection
이고,
NodeList
는 추후 DOM
이 발전하면서 생긴것같다.
둘의 차이점은 다음과 같다.
iterable | live | property & method | |
---|---|---|---|
HTMLCollection | o | o | HTMLCollection.length HTMLCollection.item() HTMLCollection.namedItem() |
NodeList | o | o and x | NodeList.length NodeList.entries() NodeList.forEach() NodeList.keys() NodeList.values() |
메서드에 대한 자세한건 아래의 링크에서 살펴보도록 하자.
MDN HTMLCollection
MDN NodeList
Table
만 보더라도 기능이 더 많이 생긴것을 볼 수 있다.
몰랐는데, 둘다 iterable
하다는것은 처음 알았네...
여기서 live
는 큰 차이점 중 하나이다.
live
란 DOM
에 값 반영시 해당 요소에 실시간으로 값이 반영되는 것이다.
HTMLCollection
으로 반환되는 list
는 live
하다.
반면, 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.length
가 2
로 된다.
이때, $lists[0]
은 item2
를 가진 li.list
가 되고
$lists[1]
은 item3
을 가진 li.list
가 된다.
그러므로 i=1
이 된 상황에서 item3
을 가진 li.list
가 선택되고, className
을 변경한다.
결국은, item2
를 가진 li
는 className
이 변경되지 않은채 존재하게 된다.
위처럼 예상치 못한 결과가 나올수 있으므로, 반복문 사용시 배열로 변경해서 사용하는것이 좋을 것 같다.
반면, static
한 NodeList
는 전혀다른 결과를 가져온다.
실시간 반영이 아니라 현재 문서에 대한 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>
*/
위 상황은 예상가능한 동작을 한다.
위 NodeList
나 HTMLCollection
을 배열로 변경하고 싶다면,
Array.from()
을 사용하면된다.
반면, NodeLIst
역시 live
한 collection
을 반환할 수 있다.
Node.childNodes
가 그렇다.
해당 요소를 반복 순회하는 일이 많다고 싶으면, 그냥 Array
객체로 만든이후 처리하는것이 현명할 듯싶다.
이렇게 오늘은 NodeList
와 HTMLCollection
의 차이를 알아보았다.
위 차이를 알아야 추후 개발시 발생할 수 있는 문제점중 하나로 기억할 수 있게 된것 같다