<aside> ✂️
→ 공통점
items[0]
)length
속성 보유for...of
문으로 순회 가능[...]
)으로 배열 변환 가능→ 중요한 특징
getElementsByTagName
, getElementsByClassName
→ 새로운 요소 추가 시 자동으로 length 업데이트→ HTMLCollection
은 언제나 live 객체로 동작한다.
→ NodeList
는 대부분의 경우 노드 객체의 상태 변화를 실시간으로 반영하지 않고 정적 상태 non-live 객체로 동작하지만, 경우에 따라 live 객체로 동작할 때가 있다.
<!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>
→ 두번째 li 요소만 class 값이 변경되지 않는다.
why ?
for
루프 첫 번째 반복:
$elems[0]
(Apple)을 class="blue"
로 변경.red
가 아니니까 컬렉션에서 자동 제외됨$elems
에는 Banana, Orange만 남음for
루프 두 번째 반복:
i = 1
→ 이 시점에 $elems[1]
은 Orange (Banana가 0번으로 당겨졌음)HTMLCollection이 live라서 루프 도중에 컬렉션이 실시간으로 변해서 Banana가 건너뛰어진 것
→ 해결 방법은?
// for 문을 역방향으로 순회
for (let i = $elems.length - 1; i >=0; i--) {
$elems[i].className = 'blue';
}
// while 문으로 HTMLCollection에 요소가 남아 있지 않을 때까지 무한 반복
let i = 0;
while ($elems.length > i) {
$elems[i].className = 'blue';
}
// HTMLCollection을 배열로 변환하여 순회
[...$elems].forEach(elem => elem.className = 'blue');
// 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>
배열로 변환하는 방법 ?
<!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>
<aside> ✂️
→ 요소 노드를 취득한 다음은 ?
<ul id="fruits">
<li class="apple">Apple</li>
<li class="banana">Banana</li>
<li class="orange">Orange</li>
</ul>
ul (id="fruits") ├─ li.apple → Apple ├─ li.banana → Banana └─ li.orange → Orange
element.parentNode
→ 부모 노드element.parentElement
→ 부모가 요소 노드일 때만 반환element.childNodes
→ 모든 자식 노드 (텍스트 포함)element.children
→ 자식 요소만 (HTMLCollection)element.firstChild
/ element.lastChild
element.firstElementChild
/ element.lastElementChild
element.previousSibling
/ element.nextSibling
→ 텍스트 포함element.previousElementSibling
/ element.nextElementSibling
→ 요소만→ 이때 노드 탐색 프로퍼티는 모두 접근자 프로퍼티다
</aside>
<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>
<ul id="fruits"><li class="apple">Apple</li><li class="banana">Banana</li><li class="orange">Orange</li></ul>
</aside>
<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>
<aside> ✂️
</aside>
<aside> ✂️
</aside>
<aside> ✂️
</aside>
<aside> ✂️
</aside>
<aside> ✂️
</aside>