티스토리 뷰

 

 

 

오늘은 querySelectorAll나 getElementsByClassName과 같이 여러 개의 요소 노드 객체를 반환할 때 사용되는 객체인 HTMLCollection과 NodeList의 차이에 대해 다룰 것이다.

 

 

우선 둘의 공통점부터 살펴보자.

 

HTMLCollection과 NodeList는 모두 유사 배열 객체이면서 이터러블이다. 따라서 둘 다 length 프로퍼티를 가지기 때문에 객체를 배열처럼 접근할 수 있고 반복문을 돌릴 수 있다.

하지만 유사 배열 객체이기 때문에 자바스크립트에서 제공하는 배열 객체의 메소드를 사용할 수 없다는 특징이 있다. (ex. map, forEach, reduce 등등)

 

(유사 배열 객체에 대한 자세한 내용은 아래 링크에서 볼 수 있다.)

https://codingtoddlerr.tistory.com/101

 

 

HTMLCollection과 NodeList


 

메소드 의미 결과
document.getElementById('id') HTML id속성으로 태그 선택하기 id에 해당하는 태그 하나
document.getElementsByClassName('class') HTML class속성으로 태그 선택하기 class에 해당하는 태그 모음(HTMLCollection)
document.getElementsByTagName('tag') HTML 태그 이름으로 태그 선택하기 tag에 해당하는 태그 모음(HTMLCollection)
document.querySelector('css') css 선택자로 태그 선택하기 css 선택자에 해당하는 태그 중 가장 첫번째 태그 하나
document.querySelectorAll('css') css 선택자로 태그 선택하기 css 선택자에 해당하는 태그 모음(NodeList)

 

- HTMLCollection 객체

 

 HTMLCollection 객체는 getElementsByTagNamegetElementsByClassName 메서드가 반환한다.

HTMLCollection 객체는 노드 객체의 상태 변화를 실시간으로 반영하는 살아있는 live DOM 컬렉션 객체이다.

여기서 '살아있다' 라는 의미는 객체가 스스로 실시간 노드 객체의 상태 변경을 반영함을 의미한다.

 

 

- NodeList 객체

 

NodeList 객체는 querySelectorAll 등의 메서드가 반환한다.

NodeList 객체는 노드 객체의 상태 변화를 반영하지 않는 non-live DOM 컬렉션 객체이므로 HTMLCollection와 다르게 노드가 변경되어도 그 상태를 변경하지 않는다.

 

 

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTMLCollection vs NodeList</title>
</head>
<body>

<ul id="myList">
  <li>Item 1</li>
  <li>Item 2</li>
</ul>

<button id="addItem">Add Item</button>

<script>
  const list = document.getElementById('myList');
  
  // HTMLCollection
  const itemsByTagName = list.getElementsByTagName('li');
  console.log(itemsByTagName.length); // Output: 2

  // NodeList
  const itemsByQuerySelectorAll = document.querySelectorAll('#myList li');
  console.log(itemsByQuerySelectorAll.length); // Output: 2

  // Add Item
  const addItemButton = document.getElementById('addItem');
  addItemButton.addEventListener('click', function() {
    const newItem = document.createElement('li');
    newItem.textContent = 'New Item';
    list.appendChild(newItem);

    console.log(itemsByTagName.length); // Output: 3 (HTMLCollection는 실시간으로 노드 객체의 상태 변경을 반영한다.)
    console.log(itemsByQuerySelectorAll.length); // Output: 2 (NodeList는 실시간으로 노드 객체의 상태 변경을 반영하지 않는다.)
  });
</script>

</body>
</html>

위 예시를 보면 알 수 있듯이 HTMLCollection는 실시간으로 노드 객체의 상태 변경을 반영하지만 NodeList는 그렇지 않다.

 

하지만 childNodes 프로퍼티가 반환하는 NodeList 객체는 다르다.

HTMLCollection 객체와 같이 실시간으로 노드 객체의 상태 변경을 반영하는 live 객체로 동작한다. 

 

또한 childeNodes는 요소 노드 뿐 만 아니라 공백 텍스트 노드(엔터 키)도 포함하고 있기 때문에 childNodes를 사용할 때 반환되는 NodeList 길이가 예상과 다를 수 있다. 아래 예시를 보면 childNodes를 사용했을 때 두 개의 요소 노드 (<p>)와 공백  텍스트 노드("Child 1"과 "Child 2" 사이의 공백), 총 3개의 노드가 반환된다.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTMLCollection vs NodeList</title>
</head>
<body>

<div id="parent">
  <p class="child">Child 1</p>
  <p class="child">Child 2</p>
</div>

<script>
  // HTMLCollection
  const childrenByClass = document.getElementsByClassName('child');
  console.log(childrenByClass.length); // Output: 2

  const parent = document.getElementById('parent');
  const childrenByTagName = parent.children;
  console.log(childrenByTagName.length); // Output: 2

  // NodeList
  const nodeListByQuerySelectorAll = document.querySelectorAll('.child');
  console.log(nodeListByQuerySelectorAll.length); // Output: 2

  const nodeListByChildNodes = parent.childNodes;
  console.log(nodeListByChildNodes.length); // Output: 3 (요소 노드 뿐 만 아니라 텍스트 노드도 포함되어 있다.)
</script>

</body>
</html>

 

 

또한, NodeList는 HTMCollection과 다르게 NodeList.prototype.forEach 메서드를 상속받아 사용할 수 있다. 그러나 forEach 외의 Array.prototype에서 제공하는 map, reduce, filter 등의 메서드는 사용할 수 없다.

 

따라서 HTMLCollection과 NodeList 모두 편리하게 사용하기 위해서는 배열로 만들어줘야 한다.

특히 HTMLCollection과 같은 live 객체는 반복문을 순회하면서 노드가 변경되는 경우, 개발자의 의도와는 다른 결과가 발생할 수 있으므로 배열로 바꾸어 사용하는 것이 바람직하다.

 

두 객체를 배열로 만드는 방법은 Array.from과 스프레트 연산자를 이용한 방법이 있다.

// Array.from() 사용 예제
const nodeList = document.querySelectorAll('.example');
const arrayFromNodeList = Array.from(nodeList);

// 스프레드 연산자 사용 예제
const nodeList = document.querySelectorAll('.example');
const arrayFromNodeList = [...nodeList];

 

둘 다 자주 사용되는 DOM 컬렉션 객체이므로 차이를 명확하게 알아두어야겠다.