| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- iife
- hooks
- 00000000
- Nextjs
- 미디어쿼리
- useState
- WebP
- 상하중앙정렬
- 즉시실행함수
- CSS
- 웹폰트최적화
- vendorprefix
- 43인치모니터
- webapi
- 스마트모니터
- 이미지확장자
- VITE
- 컨테이너쿼리
- 삼탠바이미
- LS43FM701
- react
- 반응형
- 시멘틱마크업
- 수직중앙정렬
- 벤더프리픽스
- 리액트
- CSS헥사코드
- JS슬라이더
- css 수직중앙정렬
- state끌어올리기
- Today
- Total
Nimbus
딥다이브 - DOM2(node) 본문
39.6.3 노드 생성과 추가
innerHTML 프로퍼티와 insertAdjacentHTML 메서드는 HTML 마크업 문자열을 파싱하여 노드를 생성하고 DOM에 반영한다. DOM은 노드를 직접 생성/삽입/삭제/치환하는 메서드도 제공한다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits');
// 1. 요소 노드 생성
const $li = document.createElement('li');
// 2. 텍스트 노드 생성
const textNode = document.createTextNode('Banana');
// 3. 텍스트 노드를 $li 요소 노드의 자식 노드로 추가
$li.appendChild(textNode);
// 4. $li 요소 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
$fruits.appendChild($li);
</script>
</html>
위 예제는 새로운 요소 노드를 생성 후 텍스트 노드를 생성하여 요소 노드의 자식 노드로 추가한 다음, 요소 노드를 DOM에 추가한다. 이 과정에 대해 살펴보자.
요소 노드 생성
Document.prototype.createElement(tagName) 메서드는 요소 노드를 생성하여 반환한다. createElement 메서드의 매개변수 tagName에는 태그 이름을 나타내는 문자열을 인수로 전달한다.
// 1. 요소 노드 생성
const $li = document.createElement('li');
위 그림처럼 createElement 메서드로 생성한 노드는 기존 DOM이 아닌 홀로 존재하는 상태로 생성되기 때문에 DOM에 추가하는 처리가 필요하다. 그리고 자식 노드도 가지고 있지 않은 상태다.
텍스트 노드 생성
Document.prototype.createTextNode(text) 메서드는 텍스트 노드를 생성하여 반환한다. createTextNode 메서드의 매개변수 text에는 텍스트 노드의 값으로 사용할 문자열을 인수로 전달한다.
// 2. 텍스트 노드 생성
const textNode = document.createTextNode('Banana');
텍스트 노드는 요소 노드의 자식 노드지만 요소 노드의 자식 노드로 추가되지 않고 홀로 존재하는 상태다. 따라서 생성된 텍스트 노드를 요소 노드에 추가하는 처리가 필요하다.
텍스트 노드를 요소 노드의 자식 노드로 추가
Node.prototype.appendChild(childNode) 메서드는 매개변수 childNode에게 인수로 전달된 노드를 appendchild 메서드를 호출한 노드의 마지막 자식 요소로 추가한다.
// 3. 텍스트 노드를 $li 요소 노드의 자식 노드로 추가
$li.appendChild(textNode);
위 그림처럼 appendChild 메서드를 통해 요소 노드와 텍스트 노드는 부자 관계로 연결되었지만 아직 기존 DOM에 추가되지 않은 상태다.
위 예제처럼 요소 노드의 자식 노드가 하나도 없다면 텍스트 노드 생성 후 요소 노드의 자식 노드로 텍스트 노드를 추가하는 것보다 textContent 프로퍼티를 사용하는 편이 간편하다.
// 텍스트 노드를 생성하여 요소 노드의 자식 노드로 추가
$li.appendChild(document.createTextNode('Banana'));
// $li 요소 노드에 자식 노드가 하나도 없는 위 코드와 동일하게 동작한다.
$li.textContent = 'Banana';
단, 요소 노드에 자식 노드가 있는 경우에는 textContent 프로퍼티에 문자열을 할당하면 요소 노드의 모든 자식 노드가 제거되고 할당되기 때문에 주의해야 한다.
요소 노드를 DOM에 추가
Node.prototype.appendChild 메서드를 사용하여 텍스트 노드와 부자 관계로 연결된 요소 노드를 #fruits 요소 노드의 마지막 자식 요소로 추가한다.
// 4. $li 요소 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
$fruits.appendChild($li);
이 과정으로 새롭게 생성한 요소 노드가 DOM에 추가된다. 위 예제에서 요소 노드를 생성하여 DOM에 한번 추가하므로 DOM은 한 번 변경된다. 이때 리플로우와 리페인트가 실행된다.
39.6.4 복수의 노드 생성과 추가
<!DOCTYPE html>
<html>
<body>
<ul id="fruits"></ul>
</body>
<script>
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);
});
</script>
</html>
위 예제는 3개의 요소 노드를 생성하여 DOM에 3번 추가하므로 DOM이 3번 변경된다. 이때 리플로우와 리페인트가 3번 실행되므로 이 문제를 회피하기 위해 컨테이너 요소를 사용할 수 있다. 컨테이너 요소를 미리 생성 후 DOM에 추가해야 할 3개의 요소 노드를 컨테이너 요소에 자식 노드로 추가하고, 컨테이너 요소를 #fruits 요소에 자식으로 추가한다면 DOM은 한 번만 변경된다.
하지만 컨테이너 요소를 사용하면 불필요한 컨테이너 요소(div)가 추가되므로 DocumentFragment 노드를 통해 해결 할 수 있다. DocumentFragment 노드는 문서, 요소, 어트리뷰트, 텍스트 노드와 같은 노드 객체의 일종으로, 부모 노드가 없어서 기존 DOM과는 별도로 존재한다는 특징이 있다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits"></ul>
</body>
<script>
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.appenndChild(textNode);
// 4. $li 요소 노드를 DocumentFragment 노드의 마지막 자식 노드로 추가
$fragment.appendChild($li);
});
// 5. DocumentFragment 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
$fruits.appendChild($fragment);
</script>
</html>
먼저 DocumentFragment 노드를 생성하고 DOM에 추가할 요소 노드를 생성하여 DocumentFragment 노드에 자식 노드로 추가한 다음, DocumentFragment 노드를 기존 DOM에 추가한다. 이때 DOM 변경은 한 번뿐이며 리플로우와 리페인트도 한 번만 실행된다. 따라서 여러 요소 노드를 DOM에 추가할 경우 DocumentFragment 노드를 사용하는 것이 효율적이다.
39.6.5 노드 삽입
마지막 노드로 추가
Node.prototype.appendChild 메서드는 인수로 전달받은 노드를 호출한 노드의 마지막 자식 노드로 DOM에 추가한다. 이때 노드를 추가할 위치를 지정할 수 없고 언제나 마지막 노드로 추가한다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
</ul>
</body>
<script>
// 요소 노드 생성
const $li = document.createElement('li');
// 텍스트 노드를 $li 요소 노드의 마지막 자식 노드로 추가
$li.appendChild(document.createTextNode('Orange'));
// $li 요소 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
document.getElementById('fruits').appendChild($li);
</script>
</html>
지정한 위치에 노드 삽입
Node.prototype.insertBefore(newNode, childNode) 메서드는 첫 번째 인수로 전달받은 노드를 두 번째 인수로 전달받은 노드 앞에 삽입한다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits');
// 요소 노드 생성
const $li = document.createElement('li');
// 텍스트 노드를 $li 요소 노드의 마지막 자식 노드로 추가
$li.appendChild(document.createTextNode('Orange'));
// $li 요소 노드를 #fruits 요소 노드의 마지막 자식 요소 앞에 삽입
$fruits.insertBefore($li, $fruits.lastElementChild);
// Apple - Orange - Banana
</script>
</html>
39.6.6 노드 이동
DOM에 이미 존재하는 노드를 appendChild 또는 insertBefore 메서드를 사용하여 DOM에 다시 추가하면 현재 위치에서 노드를 제거하고 새로운 위치에 노드를 추가한다. 즉, 노드가 이동한다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
<li>Orange</li>
</ul>
</body>
<script>
consnt $fruits = document.getElemetById('fruits');
// 이미 존재하는 요소 노드를 취득
const [$apple, $banana, ] = $fruits.children;
// 이미 존재하는 $apple 노드를 #fruits 요소 노드의 마지막 노드로 이동
$fruits.appedChild($apple); // Banana - Orange - Apple
// 이미 존재하는 $banana 요소 노드를 #fruits 요소의 마지막 자식 노드 앞으로 이동
$fruits.insertBefore($banana, $fruits.lastElementChild);
// Orange - Banana - Apple
</script>
</html>
39.6.7 노드 복사
Node.prototype.cloneNode([deep:ture | false]) 메서드는 노드의 사본을 생성하여 반환한다.
매개변수 deep에 true를 인수로 전달하면 노드에 깊은 복사하여 모든 자손 노드가 포함된 사본을 생성하고, false를 인수로 전달하면 얕은 복사하여 노드 자신만의 사본을 생성한다. 얕은 복사로 생성된 요소 노드는 자손 노드를 복사하지 않으므로 텍스트 노드도 없다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
</ul>
</body>
<script>
const $fruits = documemt.getElementById('fruits');
const $apple = $fruits.firstElementChild;
// $apple 요소를 얕은 복사하여 사본을 생성. 텍스트 노드가 없는 사본이 생성된다.
const $shallowClone = $apple.cloneNode();
// 사본 요소 노드에 텍스트 추가
$shallowClone.textContent = 'Banana';
// 사본 요소 노드를 #fruits 요소 노드의 마지막 노드로 추가
$fruits.appendChild($shallowClone);
// #fruits 요소를 깊은 복사하여 모든 자손 노드가 포함된 사본을 생성
const $deepClone = $fruits.cloneNode(true);
// 사본 요소 노드를 #fruits 요소 노드의 마지막 노드로 추가
$fruits.appendChild($deepClone);
</script>
</html>
39.6.8 노드 교체
Node.prototype.replaceChild(newChild, oldChild) 메서드는 자신을 호출한 노드의 자식 노드를 다른 노드로 교체한다.
replaceChild 메서드는 자신을 호출한 노드의 자식 노드인 oldChild 노드를 newChild 노드로 교체한다. 이때 oldChild 노드는 DOM에서 제거된다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits');
// 기존 노드와 교체할 요소 노드를 생성
const $newChild = document.createElement('li');
$newChild.textContent = 'Banana';
// #fruits 요소 노드의 첫 번째 자식 요소 노드를 $newChild 요소 노드로 교체
$fruits.replaceChild($newChild, $fruits.firstElementChild);
</script>
</html>
39.6.9 노드 삭제
Node.prototype.removeChild(child) 메서드는 child 매개변수에 인수로 전달한 노드를 DOM에서 삭제한다. 인수로 전달한 노드는 removeChild 메서드를 호출한 노드의 자식 노드여야 한다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits');
// #fruits 요소 노드의 마지막 요소를 DOM에서 삭제
$fruits.removeChild($fruits.lastElementChild);
</script>
</html>