우리가 여러 개의 요소 노드 객체를 가져올 때 사용하는
querySelectorAll() , getElementsByClassName() 같은 브라우저 메소드는
NodeList와 HTMLCollection를 반환합니다
HTMLCollection과 NodeList
둘 다 여러개의 DOM요소들을 불러들일때 반환되는 타입이며 유사 배열 객체이면서 이터러블입니다.
그러므로 둘 다 length 프로퍼티를 가지므로 객체를 배열처럼 접근할 수 있고 반복문을 돌 수 있습니다.
for (let i = 0; i < **childNodes**.length; i++) {
$students.removeChild(**childNodes[i]**);
}
그러나 유사 배열 객체이기 때문에 자바스크립트에서 제공하는 배열 객체의 메소드는 사용할 수 없습니다. (단, NodeList는 forEach는 사용가능)
따라서 HTMLCollection과 NodeList 모두 편리하게 사용하기 위해서는 배열로 만들어줘야 합니다.
특히 HTMLCollection과 같은 live 객체는 반복문을 순회하면서 노드가 변경되는 경우, 개발자의 의도와는 다른 결과가 발생할 수 있으므로 배열로 바꾸어 사용하는 것이 바람직합니다.
대표적으로
Array.from()
을 사용해서 두 객체를 배열로 만들 수 있습니다
(익스플로러같은 구식 브라우저에는 Array.prototype.slice.call()을 사용한다고 합니다)
이제 , NodeList와 HTMLCollection에 대해 각각 알아보고 차이점에 대해서 알아 봅시다
NodeList는
childNodes
와 같은 프로퍼티 ( children이랑 다릅니다! )querySelectorAll()
과 같은 메드에 의해 반환되는 노드의 콜렉션 입니다속성으로는
NodeList.length
메소드로는
NodeList 는 배열은 아니지만 forEach()를 사용하여 반복할 수 있습니다.
그러나 forEach 외의 Array.prototype에서 제공하는 map, reduce, filter 등의 메서드는 사용할 수 없습니다.
만약 배열 형태로 사용하고 싶다면 NodeList 은
**Array.from() 을 지원합니다. 그러므로 Array.from()을 사용하여 배열로 변환해서 사용 할 수도 있습니다**
<div class="row">
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat occupied"></div>
<div class="seat occupied"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
</div>
document.querySelectorAll('.row .seat:not(.occupied)')
Array.from(document.querySelectorAll('.row .seat:not(.occupied)'))
💡 일부 브라우저는 아직NodeList.forEach() 또는 Array.from()가
작동되지 않습니다
이럴 때는 Array.prototype.forEach()를 사용하면 됩니다
경우에 따라, NodeList는 라이브 컬렉션으로, DOM의 변경 사항을 실시간으로 컬렉션에 반영합니다.
라이브 컬렉션이란, 객체가 스스로 실시간 노드 객체의 상태 변경을 반영함을 의미합니다
var parent = document.getElementById('parent');
var child_nodes = parent.childNodes; **// 라이브 컬렉션 NodeList**
console.log(child_nodes.length); // let's assume "2"
parent.appendChild(document.createElement('div')); // add div
console.log(child_nodes.length); // should output "3"
NodeList 는 추가적으로 **HTMLCollection 리스트에는 없는 HTML 노드를 포함할 수 있습니다**
<div>
<p>Hello, World</p>
TypeScript! **// 텍스트 노드**
</div>;
const div = document.getElementByTagName("div")[0];
div.children;
// HTMLCollection(1) [p]
div.childNodes;
// NodeList(2) [p, text]
Hello, World
요소만을 포함하고 있고NodeList에서 text 부분은
**"TypeScript!" 텍스트를 포함하는 문자 그대로의 Node입니다
HTMLCollection은 이 Node를 포함하지 않습니다. `왜냐하면 HTMLElement로 간주하지 않기 때문입니다.**`
NodeList 는 HTMLElement 를 포함하여 다양한 유형의 노드를 포함할 수 있습니다
NodeList는 Node 인터페이스를 상속하므로 Node와 그 하위 개념인
HTMLElement, Text, Comment 등의 노드 유형을 모두 포함합니다
(HTML 상속 그래프를 보시면 이해가 갈 겁니다)
따라서 NodeList 타입은 단지 여러 개의 노드를 포함하는 컬렉션을 나타내며,
이 컬렉션은 다양한 유형의 노드를 포함할 수 있습니다.
예를 들어서 , querySelectorAll()로 여러 요소 노드를 가져옵니다
그렇다면 이건 NodeList형태 이지만 사실 우리는 이 리스트의 내용들이
HTML요소인 것을 확신하고 있습니다
그러므로 HTMLElement[ ] 타입으로 캐스팅이 가능한 것입니다
<circle cx="140" cy="70" r="20" class="figure-part" />
<!-- Body -->
<line x1="140" y1="90" x2="140" y2="150" class="figure-part" />
<!-- Arms -->
<line x1="140" y1="120" x2="120" y2="100" class="figure-part" />
<line x1="140" y1="120" x2="160" y2="100" class="figure-part" />
<!-- Legs -->
<line x1="140" y1="150" x2="120" y2="180" class="figure-part" />
<line x1="140" y1="150" x2="160" y2="180" class="figure-part" />
const hangman = Array.from(document.querySelectorAll('.figure-part')) as HTMLElement[];
HTMLCollection는
속성으로는
HTMLCollection.length
메소드로는
HTMLCollection 인터페이스는 요소의 문서 내 순서대로 정렬된 일반 컬렉션(arguments처럼 배열과 유사한 객체)을 나타내며 리스트에서 선택할 때 필요한 메서드와 속성을 제공합니다.
그렇지만 유사배열 형태 이므로
forEach 메소드 , Array.prototype에서 제공하는 map, reduce, filter 등의 메소드들을**사용할 수 없습니다**
HTMLCollection는 유사배열 형태이고 NodeList와 달리 forEach조차 사용할 수 없습니다
그렇지만 이 역시 Array.from() 메소드를 사용해서 배열로 변환해서
사용이 가능합니다
<div id="app">
<h1>test</h1>
<div class="greeting">Hello</div>
<div class="greeting">Hello</div>
<div class="greeting">Hello</div>
</div>
const greeting = document.getElementsByClassName('greeting');
console.log(Array.from(greeting));
HTMLCollection은 노드 객체의 상태 변화를 실시간으로 반영하는 살아있는 라이브 컬렉션 객체입니다.
<div id="app">
<h1>test</h1>
<div class="greeting">Hello</div>
</div>
const app = document.getElementById('app');
const greeting = document.getElementsByClassName('greeting');
// HTMLCollection [div.greeting] 1
console.log(greeting, greeting.length);
app.insertAdjacentHTML('beforeend', '<div class="greeting">Hello</div>');
// HTMLCollection(2) [div.greeting, div.greeting] 2
console.log(greeting, greeting.length);
처음 greeting이라는 class 명을 가진 요소는 하나 밖에 없습니다
따라서 처음에는 길이가 1인 HTMLCollection을 출력합니다
이후에 greeting이라는 class 명을 가진 요소를 추가하면 길이가 2인 HTMLCollection를 출력합니다.
분명히 greeting을 const로 선언하였고 재선언하거나 재할당하지도 않지만 값이 변경되었습니다.
이는 HTMLCollection이 live 객체이기 때문에 요소 노드의 추가나 삭제를 바로 반영해주기 때문입니다.
NodeListOf는 NodeList 객체를 제네릭으로 래핑한 타입입니다.
이 타입은 NodeList와 유사하게 여러 개의 Node 객체를 포함하는 노드 리스트를 나타내지만, T 타입 인수를 통해 노드의 타입을 명시할 수 있습니다.
예를 들어, NodeListOf 타입은 여러 개의 HTMLDivElement 객체를 포함하는 노드 리스트를 나타냅니다.
따라서, NodeListOf 타입은 제네릭을 사용하여 더 안전한 타입 체킹을 할 수 있으며, 런타임에서 발생할 수 있는 타입 관련 버그를 미연에 방지할 수 있습니다.
반면, NodeList 타입은 단순한 리스트 형태로 사용되기 때문에, 보다 간단하게 사용할 수 있습니다.
출처 :