DOM 2
DOM 모자딥다 추가정리
getElementsByTagName, getElementsByClassName 메서드가 반환하는 HTMLCollection 객체는 노드
객체의 상태 변화를 실시간으로 반영하는 살아 있는 (live) DOM 컬렉션 객체다.
const { childNodes } = $something; // childNodes 프로퍼티 또한 NodeList 객체(live)를 반환한다.
<ul id="fruits">
<li class ="red">Apple</li>
<li class ="red">Banana</li>
<li class ="red">Orange</li>
</ul>
const $elem = document.getElementsByClassName("red");
// 이 시점에 HTMLCollection 객체이는 3개의 요소 노드가 담겨 있다.
console.log($elem); // HTMLCollection(3) [li.red, li.red, li.red]
// HTMLCollection 객체의 모든 요소의 class 값을 "blue"로 변경한다.
for (let i = 0; i < $elem.length; i++) {
$elem[i].className = "blue";
}
// HTMLCollection 객체의 요소가 3개에서 1개로 변경되었다.
console.log($elem); // HTMLCollection(1) [li.red]
위 코드의 의도는 li 요소의 class 값을 전부 "blue" 로 변경 하는것인데, 결과는 두개만 변경되었다.
그 이유는 $elem이 live DOM 컬렉션 객체이기 때문이다.
- 첫 번째 반복문에서 $elem[0]은 첫번째 li요소를 blue로 변경하고 getElementsByClassName 메서드의 인자로 전달한 "red" 와 더는 일치하지 않기 때문에 $elem에서 실시간으로 제거된다.
- 첫 번째 반복문에서 첫 번째 li요소는 $elem에서 제거됬기 때문에, 두 번째 반복문에서 $elem[1]은 세번째 li 요소가 된다.
그러면 어떻게 해결해야 할까?
- for 문을 역방향으로. (let i = $elem.length - 1; i >= 0; i--)
- while 문 사용.
- HTMLCollection 객체를 배열로 변환.
- querySelectorAll 사용.
복수의 노드 생성과 추가 주의점
<body>
<ul id="fruits"></ul>
</body>
const $fruits = document.getElementById("fruits");
['Apple', 'Banana', 'Orange'].forEach(text => {
// 1. 요소 노드 생성
const $li = document.createElement('li');
// 2. 텍스트 노드 생성
const textNode = document.createTextNode(text);
// 3. 텍스트 노드를 $li 요소 노드의 자식 노드로 추가
$li.appendChild(TextNode);
// 4. $li 요소 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
$fruits.appendChild($li);
});
3개의 요소 노드를 생성하여 DOM에 3번 추가할때 DOM은 3번 변경된다.
이때 리플로우와 리페인트가 3번 실행된다. (높은 비용이 드는 처리이다 === 비효율적이다)
그렇다면 위의 과정에서 3개의 요소를 컨테이너 요소에 자식 노드로 추가하고
컨테이너를 $fruits의 자식 노드로 추가한다면?
DOM은 한번만 변경하므로 성능에 유리하지만 불필요한 컨테이너 요소(div)가 DOM에 추가된다.
이는 바람직하지 않다... 어떻게 해야할까?
이러한 문제는 DocumentFragment 노드를 통해 해결할 수 있다!!
DocumentFragment 노드는 문서, 요소, 어트리뷰트, 텍스트 노드와 같은 노드 객체의 일종으로 부모 노드가 없어서 기존 DOM과는 별도로 존재한다는 특징이 있다.
즉, DocumentFragment 노드에 자식 노드를 추가해도 기존 DOM에는 어떠한 변경도 발생하지 않는다.
또한 DocumentFragment 노드를 DOM에 추가하면 자신은 제거되고 자식 노드만 DOM에 추가된다.
<body>
<ul id="fruits"></ul>
</body>
const $fruits = document.getElementById("fruits");
// DocumentFragment 노드 생성
const $fragment = document.createDocumentFragment();
['Apple', 'Banana', 'Orange'].forEach(text => {
// 1. 요소 노드 생성
const $li = document.createElement('li');
// 2. 텍스트 노드 생성
const textNode = document.createTextNode(text);
// 3. 텍스트 노드를 $li 요소 노드의 자식 노드로 추가
$li.appendChild(TextNode);
// 4. $li 요소 노드를 DocumentFragment 노드의 마지막 자식 노드로 추가
$fragment.appendChild($li);
// 5. DocumentFragment 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
$fruits.appendChild($fragment);
});
여러 요소 노드를 DOM에 추가하는 경우 DocumentFragment 노드를 사용하는 것이 효율적이다.
(리플로우와 리페인트가 한 번만 실행된다)