39.2.6 HTMLCollection과 NodeList

<aside> ✂️

HTMLCollection 과 NodeList

→ 공통점

  1. 유사 배열 객체
  2. 이터러블 (iterable)

→ 중요한 특징

  1. 살아있는 객체 (Live Object)

HTMLCollection은 언제나 live 객체로 동작한다.

NodeList는 대부분의 경우 노드 객체의 상태 변화를 실시간으로 반영하지 않고 정적 상태 non-live 객체로 동작하지만, 경우에 따라 live 객체로 동작할 때가 있다.

→ HTMLCollection

<!DOCTYPE html>
<head>
	<style>
		.red { color: red; }
		.blue { color: blue; }
	</style>
</head>
<html>
	<body>
		<ul id="fruits">
			<li class="red">Apple</li>
			<li class="red">Banana</li>
			<li class="red">Orange</li>
		</ul>
		<script>
			// class 값이 red인 요소 노드 모두 탐색 후 HTMLCollection 객체에 담아 반환 
			const $elems = document.getElementsByClassName('red');
			// 이 시점에 HTMLCollection 객체에는 3개의 요소 노드가 담겨있다.
			console.log($elems); // HTMLCollection(3) [li.red, li.red, li.red]
			
			// HTMLCollection 객체의 모든 요소의 class 값을 'blue'로 변경
			for(let i = 0; i< $elems.length; i++) {
				$elems[i].className = 'blue';
			}
			
			// 이 시점에서 HTMLCollection 객체에는 3개에서 1개로 변경
			console.log($elems); // HTMLCollection(1) [li.red]
		</script>

image.png

→ 두번째 li 요소만 class 값이 변경되지 않는다.

why ?

  1. for 루프 첫 번째 반복:
  2. for 루프 두 번째 반복:

HTMLCollection이 live라서 루프 도중에 컬렉션이 실시간으로 변해서 Banana가 건너뛰어진 것

→ 해결 방법은?

  1. for 문을 역방향으로 순회하는 방법으로 회피할 수 있다.
// for 문을 역방향으로 순회
for (let i = $elems.length - 1; i >=0; i--) {
	$elems[i].className = 'blue';
}
  1. while 문을 사용하여 HTMLCollection 객체에 노드 객체가 남아 있지 않을 때까지 무한 반복하는 방법으로 회피하기.
// while 문으로 HTMLCollection에 요소가 남아 있지 않을 때까지 무한 반복
let i = 0;
while ($elems.length > i) {
	$elems[i].className = 'blue';
}
  1. 유사 배열이자 이터러블인 HTMLCollection을 스프레드 문법으로 배열로 변환하여 순회
// HTMLCollection을 배열로 변환하여 순회
[...$elems].forEach(elem => elem.className = 'blue');

→ NodeList

// querySelectorAlldms DOM 컬렉션 객체인 NodeList를 반환한다.
const $elems = document.querySelectorAll('.red');

// NodeList 객체는 NodeList.prototype.forEach 메서드를 상속받아 사용한다.
$elems.forEach(elem => elem.className = 'blue');

→ NodeList 객체는 대부분의 경우 노드 객체의 상태 변경을 실시간으로 반영하지 않고 과거의 정적 상태를 유지하는 non-live 객체로 동작한다.

<!DOCTYPE html>
<html>
	<body>
		<ul id="fruits">
			<li>Apple</li>
			<li>Banana</li>
			<li>Orange</li>
		</ul>
	</body>
	<script>
	const $fruits = document.getElementById('fruits');
	
	// childNodes 프로퍼티는 NodeList 객체(live)를 반환한다.
	const { childNodes }  $fruits;
	console.log(childNodes instanceof NodeList); // true
	
	// $fruits 요소의 자식 노드는 공백 텍스트 노드를 포함해 5개다.
	console.log(childNodes); // NodeList(5) [text, li, text, li, text]
	
	for(let i = 0; i < childNodes.length; i++) {
		// removeChild 메서드는 $fruits 요소의 자식 노드를 DOM에서 삭제한다.
		// removeChild 메서드가 호출될 때마다 NodeList 객체인 childNode가 실시간 변경
		// 따라서 첫 번째, 세 번째, 다섯 번째 요소만 삭제된다.
		$fruits.removeChild(childNodes[i]);
	}
	
	// 예상과 다르게 $fruits 요소의 모든 자식 노드가 삭제되지 않는다.
	console.log(childNodes); // NodeList(2) [li, li]
	</script>
</html>

배열로 변환하는 방법 ?

  1. 스프레드 문법 이용하기
  2. Array.from 메서드 이용하기
<!DOCTYPE html>
<html>
	<body>
		<ul id="fruits">
			<li>Apple</li>
			<li>Banana</li>
			<li>Orange</li>
		</ul>
	</body>
	<script>
	const $fruits = document.getElementById('fruits');
	
	// childNodes 프로퍼티는 NodeList 객체(live)를 반환한다.
	const { childNodes }  $fruits;
	console.log(childNodes instanceof NodeList); // true
	
	// 스프레드 문법을 사용하여 NodeList 객체를 배열로 변환한다.
	[...childNodes].forEach(childNode => {
		$fruits.removeChild(childNode);
	});
	
	// $fruits 요소의 모든 자식 노드가 삭제된다
	console.log(childNodes); // NodeList[]
	</script>
</html>

</aside>

39.3 노드 탐색

<aside> ✂️

→ 요소 노드를 취득한 다음은 ?

  1. DOM 트리를 옮겨 다니며 부모, 형제, 자식 노드를 탐색한다.
<ul id="fruits">
	<li class="apple">Apple</li>
	<li class="banana">Banana</li>
	<li class="orange">Orange</li>
</ul>
  1. 노드를 탐색한다 → HTML 문서 구조는 트리(Tree) 형태!

ul (id="fruits") ├─ li.apple → Apple ├─ li.banana → Banana └─ li.orange → Orange

  1. 프로퍼티들을 이용하여 부모, 자식, 형제 노드를 탐색하기

→ 이때 노드 탐색 프로퍼티는 모두 접근자 프로퍼티다

</aside>

39.3.1 공백 텍스트 노드

<aside> ✂️

→ HTML 요소 사이의 스페이스, 탭, 줄바꿈(개행) 등의 공백 문자는 텍스트 노드를 생성한다.

이를 공백 텍스트 노드라 한다.

<!DOCTYPE html>
<html>
	<body>
		<ul id="fruits">
			<li class="apple">Apple</li>
			<li class="banana">Banana</li>
			<li class="orange">Orange</li>
		</ul>
	</body>
</html>

39-12.png

<ul id="fruits"><li class="apple">Apple</li><li class="banana">Banana</li><li class="orange">Orange</li></ul>

</aside>

39.3.2 자식 노드 탐색

<aside> ✂️

→ 자식 노드 탐색을 위한 노드 탐색 프로퍼티 확인하기

| --- | --- | --- | --- |

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li class="apple">Apple</li>
      <li class="banana">Banana</li>
      <li class="orange">Orange</li>
    </ul>

    <script>
      // ul 요소 노드를 가져옴
      const $fruits = document.getElementById('fruits');

      // -------------------------------
      // 1. Node.prototype.childNodes
      // -------------------------------
      // 자식 노드를 모두 탐색하여 NodeList로 반환
      // NodeList에는 텍스트 노드(공백, 줄바꿈)도 포함됨
      console.log($fruits.childNodes);
      // NodeList(7) [text, li.apple, text, li.banana, text, li.orange, text]

      // -------------------------------
      // 2. Element.prototype.children
      // -------------------------------
      // 자식 노드 중 "요소 노드"만 탐색하여 HTMLCollection으로 반환
      // 텍스트 노드나 주석 노드는 제외됨
      console.log($fruits.children);
      // HTMLCollection(3) [li.apple, li.banana, li.orange]

      // -------------------------------
      // 3. Node.prototype.firstChild
      // -------------------------------
      // 첫 번째 자식 노드를 반환 (텍스트 노드 포함 가능)
      console.log($fruits.firstChild);
      // #text (줄바꿈/공백 때문에 첫 번째 자식은 텍스트 노드)

      // -------------------------------
      // 4. Element.prototype.firstElementChild
      // -------------------------------
      // 첫 번째 자식 "요소 노드"만 반환
      console.log($fruits.firstElementChild);
      // <li class="apple">Apple</li>

      // -------------------------------
      // 5. Node.prototype.lastChild
      // -------------------------------
      // 마지막 자식 노드를 반환 (텍스트 노드 포함 가능)
      console.log($fruits.lastChild);
      // #text (마지막 </li> 뒤에 줄바꿈 때문에 텍스트 노드)

      // -------------------------------
      // 6. Element.prototype.lastElementChild
      // -------------------------------
      // 마지막 자식 "요소 노드"만 반환
      console.log($fruits.lastElementChild);
      // <li class="orange">Orange</li>
    </script>
  </body>
</html>

</aside>

39.3.3 자식 노드 존재 확인

<aside> ✂️

</aside>

39.3.4 요소 노드의 텍스트 노드 탐색

<aside> ✂️

</aside>

39.3.5 부모 노드 탐색

<aside> ✂️

</aside>

39.3.5 형제 노드 탐색

<aside> ✂️

</aside>

39.4 노드 정보 취득

<aside> ✂️

</aside>

39.5 요소 노드의 텍스트 조작

39.5.1 nodeValue