📝

Chapter 2. 트리

 
본격적으로 DOM에 대해 알아보기 전에 앞 장에서 나온 노드에 관해 더 자세히 이해하고 DOM의 구조를 탐험하는 시간을 가져보겠습니다.

1. 노드

1.1. 노드의 개념

앞 장에서 자바스크립트 리모콘으로 웹사이트라는 로봇을 다루려면 DOM 모델의 구성 요소를 제어해야 한다고 했습니다. 이 구성 요소를 노드(Node)라고 칭합니다.
노드는 추상적인 개념이라 정확한 상을 가지고 이해하기는 어려울 수 있습니다. 그러므로 먼저 노드 개체를 통해 직관적인 접근을 해보겠습니다.
아래에 앞으로 여러분이 많이 보게 될 두 분류의 사진을 준비했습니다. 여기에서 노드가 무엇인지 가리킬 수 있나요?
notion imagenotion image
notion imagenotion image
 
우선 왼쪽 사진을 살펴보겠습니다. head 태그, title 태그, 그 안에 텍스트 뿐만 아니라 태그 사이 공백(개행)도 문서의 일부로 볼 수 있어야 합니다. 웹 페이지에 그려지지 않는 주석도 마찬가지입니다.
오른쪽 사진은 옆의 마크업에서 찾아낸 노드를 DOM 트리 구조로 시각화한 것입니다. 이처럼 브라우저는 HTML 문서의 모든 컨텐츠를 각개의 노드 개체로 표현하고 있습니다.
 
HTML 문서는 콘텐츠 > 콘텐츠 > 콘텐츠 와 같은 형태로 캡슐화 되어 있고 브라우저는 이 계층을 해석해서 트리 구조로 표현합니다. 이것이 바로 DOM tree이며 노드(Node, 나무의 마디)는 트리를 구성하는 하나하나의 개체입니다. 정리하자면 DOM은 노드 개체를 계층적으로 구조화한 트리입니다.
각각의 노드는 나무에 빗대어 루트/브랜치/리프 노드로 표현하기도 하고, 계층 관계에 따라 부모/자식/형제 노드로 표현하기도 합니다[1].
노드의 관계에 따라 달라지는 여러가지 명칭노드의 관계에 따라 달라지는 여러가지 명칭
노드의 관계에 따라 달라지는 여러가지 명칭
 

1.2. 노드의 유형

노드에는 여러 유형이 존재합니다. 각 노드 유형의 프로퍼티와 메서드가 다르므로 정확한 조작을 위해서는 유형별 특성을 알고 구분할 줄 알아야 합니다.
다음은 모든 노드 유형을 불러오는 JavaScript 코드입니다.
for (const key in Node) { console.log(key + ':' + Node[key]) }
노드 유형과 상수키노드 유형과 상수키
노드 유형과 상수키
 
이를 콘솔에 입력하면 위의 사진처럼 수많은 이름과 숫자의 쌍이 나열됩니다.
챕터 1에서 살펴본 문서 노드(Document node), 요소 노드(Element node), 어트리뷰트 노드(Attribute node), 문자 노드(Text node)는 전체 노드 유형의 일부이며, Front-End 작업 중에 자주 접하게 되는 대표 노드들입니다. 실용적인 학습을 위해 우리는 다음의 일반적인 4가지 유형만 살펴보도록 하겠습니다.

1.2.1. 문서 노드(Document node)

문서 노드는 DOM 인터페이스의 시작점이며, HTML 문서의 최상위이자 문서 전체를 나타내는 단일 노드입니다. 루트 노드이므로 부모 노드는 없지만 자식 노드를 통해 <!DOCTYPE html>, <html lang="ko">, <head>, <body>를 반환합니다. 또한 제목, URL, 최종 수정일 등 HTML 문서에 대한 전역적인 정보에 접근할 수 있게 해 줍니다. window.document 또는 document로 접근할 수 있습니다.

1.2.2. 요소 노드(Element node)

요소 노드는 HTML 요소에 대응하여 속성 노드를 가질 수 있는 유일한 노드입니다. 대부분 브라우저에 시각적으로 나타날 수 있는 형태를 가지고 있습니다. 요소 간 중첩 관계를 반영해 DOM 트리가 구성되므로 요소 노드는 문서의 구조를 표현하는 역할을 합니다. 후에 나올 노드 취득과 탐색에서 실습을 통해 자세하게 다뤄보도록 하겠습니다.
💡
JavaScript 명령어 console.log()과 console.dir()의 차이[2] 두 명령어의 결과는 거의 비슷합니다. 그러나 괄호 안 인자로 요소 노드가 적용될 경우, console.log()는 DOM 트리 구조를 출력하고, console.dir()는 JSON 트리 구조로 출력, 객체의 프로퍼티를 확인할 수 있도록 합니다.

1.2.3. 어트리뷰트 노드(Attribute node)

어트리뷰트 노드는 HTML 요소의 속성을 나타내는 노드입니다.
💡
현재는 사용이 금지되었음에 유의해야 합니다. MDN에서 🗑️(deprecated) 표시를 확인할 수 있습니다[3].

1.2.4. 텍스트 노드(Text node)

HTML의 모든 문자와 공백, 개행이 텍스트로 취급되어 텍스트 노드를 생성합니다. 텍스트 노드는 문서의 정보를 표현하며 리프 노드이므로 자식 노드를 가질 수 없습니다. 참조 및 접근을 위해서는 해당 텍스트 콘텐츠를 가지고 있는 요소 노드에 먼저 접근해야 합니다. 참고로 주석 노드(Comment node)도 텍스트 노드와 성격이 유사합니다.

1.2.5. 노드 유형 식별

마지막으로 노드의 정보를 확인하는 데 도움을 주는 프로퍼티(Property)를 안내하겠습니다.
// 예제 1 let target = document.body; console.log(target.nodeType); // 위 코드에서 target을 변경해주세요.
JavaScript에서 노드 유형 객체의 키(key)는 숫자 데이터입니다. .nodeType을 이용하면 해당 상수를 알아낼 수 있습니다. 어떤 유형에 어떤 상수가 배정되어 있는지는 위의 ‘노드 유형과 상수키’ 사진을 참고해주세요.
// 예제 2 let target = document.body; console.log(target.nodeType === Node.ELEMENT_NODE); // 위 코드에서 target을 변경해주세요.
이러한 상수 목록을 외우기는 쉽지 않은 일이기에 예제 2와 같은 방법을 사용할 수도 있습니다. 우항의 노드 유형은 대문자와 언더바로 작성하는 것에 주의해주세요.
 

2. DOM 트리

2.1. DOM 트리의 개념

앞서 살펴봤듯이, HTML 문서의 각 요소들은 브라우저 렌더링 엔진에 의해 파싱되어 노드 객체로 변환됩니다. 이후 각 노드 객체들은 HTML 요소가 지닌 계층적 구조에 의해 트리 구조로 표현될 수 있습니다. 이처럼 문서 내의 모든 노드 객체들이 트리 구조로 구성되어 있기 때문에 DOM을 DOM tree라고도 부릅니다[4].
 

2.1.1. 트리 자료구조

트리 구조는 다양한 자료 구조 유형 중 하나입니다. DOM에서 트리 구조는 브라우저 렌더링, 화면에 요소들을 출력하기 위해 사용합니다[5].
트리 구조는 다양한 애플리케이션에서 계층적인 구조를 가진 데이터를 표현하거나 조작하는 데 적용되는 대표적인 자료구조입니다[6]. 컴퓨터 과학에서 통용되는 용어인 트리 구조는 일반적으로 나무를 거꾸로 뒤집어 놓은 형태입니다. 여기서 뿌리를 루트라고 하며, 루트 노드는 보통 한개의 노드입니다.
루트 노드에서 출발하는 모든 노드들은 루트로부터 멀어지는 방향으로 전개됩니다. 또한 루트 노드를 제외한 모든 노드는 부모 노드를 하나만 가질 수 있고, 사이클 회로(출발한 노드에서 끝나는 경로)를 갖지 않습니다. 이 트리 구조를 탐색하는 과정을 순회(traversal)라고 합니다[7].
 

2.2. DOM과 BOM

브라우저 프로그램은 텍스트 파일인 HTML을 파싱한 결과물인 DOM을 렌더링해서 사용자에게 보여주는 역할을 합니다. 현재 다양한 웹 브라우저 개발사들이 경쟁적으로 자사의 브라우저를 개발하여 공급하고 있습니다. 이렇게 출시된 브라우저들은 DOM과 달리 정해진 표준 없이 각기 독창적인 브라우저 환경을 구성하여 제공합니다. 이것이 BOM(Brouser Object Model, 브라우저 객체 모델)입니다.
BOM은 웹 브라우저를 이루는 핵심적인 JavaScript 구조라고 할 수 있습니다. BOM은 웹 페이지의 콘텐츠와는 기능적으로 독립적인 객체들을 제공하는데, 특히 BOM의 객체 중 window는 W3C[8]에서 정의하는 window 인터페이스를 구현하는 핵심 객체입니다. window 객체는 사용자가 웹 브라우저를 사용할 때 띄워주는 창(window)을 가리키며, DOM이 바로 이 window의 프로퍼티로 작동하게 됩니다. 따라서 웹 페이지를 이루는 노드 객체들은 DOM의 최상단 루트 노드인 Document에 속하며, 이 Document는 브라우저의 window에 속하게 됩니다.
정리하면 BOM이 제공하는 window 객체는 웹 브라우저에 의해 파싱된 HTML 노드들의 루트 노드, 즉 가장 상위의 부모 노드가 되는 전역 객체입니다. 그리고 DOM은 자신의 원본 문서인 HTML을 파싱해주는 브라우저 렌더링 엔진이 제공하는 BOM의 내부 프로퍼티로서 동작합니다[9].
 

2.3. 트리 구조 실습

DOM tree 구조로 표현한 실습 코드입니다.DOM tree 구조로 표현한 실습 코드입니다.
DOM tree 구조로 표현한 실습 코드입니다.
 
그러면 이제 직접 브라우저 환경에서 트리 구조를 들여다봅시다. 내용이 없는 빈 HTML 문서에 <ul>태그를 작성하고, <ul>태그의 자식으로 <li>태그를 넣고, <li>태그 안에 “Hello World!” 텍스트를 넣어주도록 하겠습니다.
 
<!DOCTYPE html> <head></head> <body> <ul> <li>Hello World!</li> </ul> </body> </html>
notion imagenotion image
 
빈 문서에 <ul>태그, <ul>태그의 자식으로 <li>태그를 넣었습니다. <li>태그의 자식으로 “Hello World!” 텍스트가 들어가 있습니다.
구조를 살펴보기 위해 개발자 도구에서 콘솔 탭을 클릭해, window 전역 객체를 열어 보겠습니다.
 
window 객체 안을 들여다보면 document 프로퍼티를 확인할 수 있습니다.window 객체 안을 들여다보면 document 프로퍼티를 확인할 수 있습니다.
window 객체 안을 들여다보면 document 프로퍼티를 확인할 수 있습니다.
 
document 프로퍼티를 열어보면 document 역시 객체임을 확인할 수 있으며, document 객체의 자식 노드(childNodes)가 1개 존재함을 알 수 있습니다.
 
document 노드의 자식 노드document 노드의 자식 노드
document 노드의 자식 노드
document 노드의 자식 노드는 html입니다. html 노드를 열어 보겠습니다.
 
html 노드의 자식 노드html 노드의 자식 노드
html 노드의 자식 노드
html 노드의 자식 노드는 head, body가 있습니다. 우리가 작성한 <ul>태그를 찾아가기 위해 body 노드의 childNodes를 열어 보겠습니다.
 
body 노드의 자식 노드body 노드의 자식 노드
body 노드의 자식 노드
body 노드의 자식 노드는 text, ul, text가 있습니다. 태그를 작성할 때 개행을 했기 때문에 앞뒤로 text 노드가 추가되어 있습니다. 다음은 ul 노드의 childNodes로 들어가 <li> 태그를 찾아보겠습니다.
 
ul 노드의 자식 노드ul 노드의 자식 노드
ul 노드의 자식 노드
body 노드의 자식 노드는 text, ul, text가 있습니다. 태그를 작성할 때 개행을 했기 때문에 앞뒤로 text 노드가 추가되어 있습니다. 다음은 ul 노드의 childNodes로 들어가 <li> 태그를 찾아보겠습니다.
ul 노드의 자식 노드는 text, li, text가 있습니다. 태그를 작성할 때 개행을 했기 때문에 앞뒤로 text 노드가 추가되어 있습니다. 드디어 li 노드가 보입니다. li 노드 안에 우리가 작성한 “Hello World!” 가 들어 있겠군요.
 
li 노드의 자식으로 존재하는 text 노드 li 노드의 자식으로 존재하는 text 노드
li 노드의 자식으로 존재하는 text 노드
li 노드를 열어보니 text 노드가 자식으로 들어 있습니다. text 노드를 열어보면 “Hello World!” 라는 nodeValue를 만날 수 있습니다. text 노드는 리프 노드이기 때문에 자식 요소는 존재하지 않습니다.
 

3. 노드 취득 / 컬렉션 객체

3.1. DOM 구조에 접근하기

DOM은 JavaScript와 같은 스크립트 언어가 DOM 구조에 접근하여 문서의 구조, 스타일, 내용을 변경할 수 있도록 합니다[10]. 이처럼 문서를 동적으로 조작하기 위해선 문서를 나타내는 document 객체에 접근하여 요소 노드를 먼저 취득해야 합니다.
DOM이 제공하는 대표적인 메서드를 사용하여 요소 노드를 취득하는 방법을 알아보겠습니다.
 

3.2. 요소 노드를 취득하는 방법

3.2.1. ID로 HTML 요소 찾기

document.getElementById() 메서드는 지정된 ID 어트리뷰트 값을 가진 요소 노드 1개를 찾아 반환합니다. ID 값은 HTML 문서 내에서 유일해야 하기 때문에 특정 요소를 빠르게 찾을 때 유용합니다[11]. 만약 문서 내에 해당 ID 값을 가진 요소가 없다면 null을 반환합니다.
<!DOCTYPE html> <html> <head> <title>getElementById 예제</title> </head> <body> <ul> <li id="croissant">크루아상</li> <li id="madeleine">마들렌</li> <li id="doughnut">도넛</li> <li id="yakgwa">약과</li> </ul> <script> // id 값이 'madeleine'인 요소 노드를 찾아 반환합니다. const favorite = document.getElementById('madeleine'); // 취득한 요소 노드의 style.color 프로퍼티 값을 'orange'로 변경합니다. favorite.style.color = 'orange'; </script> </body> </html>
notion imagenotion image

3.2.2 태그 이름으로 HTML 요소 찾기

document.getElementsByTagName() 메서드는 지정된 태그 이름을 가진 모든 요소 노드를 찾아 반환합니다. 이때 찾은 요소 노드들을 DOM 컬렉션 객체인 HTMLCollection 객체에 담아 반환합니다. HTMLCollection 객체는 여러 개의 노드 목록을 나타내는 유사 배열 객체(Array-like object)이며 이터러블(iterable, 순회 가능)입니다. 만약 문서 내에 해당 태그 이름을 가진 요소가 없다면 빈 HTMLCollection 객체를 반환합니다. 다음에 나올 [3.3. DOM 컬렉션] 파트에서 자세히 살펴보겠습니다.
그리고 document.getElementsByTagName() 메서드의 인수에 ‘*’ 를 작성하면 모든 요소 노드를 취득할 수 있습니다.
<!DOCTYPE html> <html> <head> <title>getElementsByTagName 예제</title> </head> <body> <ul> <li id="croissant">크루아상</li> <li id="madeleine">마들렌</li> <li id="doughnut">도넛</li> <li id="yakgwa">약과</li> </ul> <script> // 태그 이름이 'li'인 요소 노드를 모두 찾아 반환합니다. const desserts = document.getElementsByTagName('li'); // 문서 내의 모든 요소 노드를 찾아 반환합니다. const all = document.getElementsByTagName('*'); </script> </body> </html>
notion imagenotion image

3.2.3 Class로 HTML 요소 찾기

document.getElementsByClassName() 메서드는 지정된 class 어트리뷰트 값을 가진 모든 요소 노드를 찾아 반환합니다. 위에서 살펴본 getElementsByTagName() 메서드와 같이 DOM 컬렉션 객체인 HTMLCollection 객체를 반환합니다. 마찬가지로 문서 내에 해당 class 값을 가진 요소가 없다면 빈 HTMLCollection 객체를 반환합니다.
<!DOCTYPE html> <html> <head> <title>getElementsByClassName 예제</title> </head> <body> <ul> <li class="hungary">크루아상</li> <li class="france">마들렌</li> <li class="usa">도넛</li> <li class="korea">약과</li> </ul> <div class="korea">붕어빵</div> <script> // class 값이 'korea'인 요소 노드를 모두 찾아 반환합니다. const koreanDesserts = document.getElementsByClassName('korea'); </script> </body> </html>
notion imagenotion image
 

3.2.4. CSS 선택자로 HTML 요소 찾기

CSS 선택자를 사용하여 DOM에서 요소 노드를 쉽게 찾을 수 있습니다[12].
document.querySelector() 메서드는 지정된 CSS 선택자와 일치하는 첫 번째 요소 노드 1개를 찾아 반환합니다. 만약 일치하는 요소가 없다면 null을 반환합니다.
document.querySelectorAll() 메서드는 지정된 CSS 선택자와 일치하는 모든 요소 노드를 찾아 반환합니다. 이때 DOM 컬렉션 객체인 NodeList 객체를 반환합니다. NodeList 객체는 노드의 목록을 나타내는 유사 배열 객체이며 이터러블입니다. 만약 일치하는 요소가 없다면 빈 NodeList 객체가 반환됩니다. 다음에 나올 [3.3. DOM 컬렉션] 파트에서 HTMLCollection 객체와 비교하여 알아보겠습니다.
그리고 CSS 선택자 메서드는 요소를 찾을 선택자를 콤마(,)로 연결하여 여러 개를 작성할 수 있으나, document.querySelector()는 메서드 내 인자의 첫 번째 요소만 반환합니다.
<html> <head> <title>CSS 선택자 예제</title> </head> <body> <ul id="dessertsList"> <li class="hungary europe">크루아상</li> <li class="france europe">마들렌</li> <li class="usa">도넛</li> <li class="korea">약과</li> </ul> <script> // class 값이 'europe'인 첫 번째 요소 노드를 찾아 반환합니다. const europeanDessert = document.querySelector('.europe'); // id 값이 'dessertsList'인 요소의 자식 요소인 li 요소를 모두 찾아 반환합니다. const list = document.querySelectorAll('#dessertsList > li'); // class 값이 'korea'인 요소와 클래스 값이 'france'인 요소를 모두 찾아 반환합니다. const cookies = document.querySelectorAll('.korea, .france'); </script> </body> </html>
notion imagenotion image
 

3.3. DOM 컬렉션

3.3.1. 특징

HTMLCollection과 NodeList는 여러 개의 노드(요소) 목록을 나타내는 DOM 컬렉션 객체입니다. 다음과 같은 특징과 차이점이 있습니다.
iterable 유무
live 객체 유무
사용 가능한 프로퍼티/메서드
HTMLCollection
O
O
length item() namedItem()
NodeList
O
X (단, childNodes 프로퍼티의 반환 값은 live 객체로 동작)
length item() forEach() entries() keys() values()
DOM 컬렉션 객체는 유사 배열 객체이며 이터러블입니다. 배열처럼 인덱스로 목록 내의 노드에 접근할 수 있고 length 프로퍼티를 가집니다. 인덱스는 0부터 시작합니다.
DOM 컬렉션 객체의 중요한 특징은 객체의 변경 사항을 실시간으로 반영하는 라이브(live) 객체라는 점입니다. 단, HTMLCollection은 항상 라이브 객체지만 NodeList는 정적 객체이며 특정 상황에서만 라이브 객체로 동작합니다[13].
DOM 컬렉션 객체는 배열이 아니기 때문에 배열 메서드를 사용할 수 없습니다[14]. 배열의 다양한 메서드를 사용하기 위해서는 Array.from() 메서드나 전개 구문(Spread syntax, 스프레드 문법)을 사용해 배열로 변환하여 사용하는 것을 권장합니다.
 

3.3.2. HTMLCollection

HTMLCollection은 getElementsByTagName()getElementsByClassName() 메서드가 반환하는 라이브 DOM 컬렉션 객체입니다. 따라서 노드가 수정되거나 삭제되는 등의 변화가 일어나면 해당 노드가 속한 HTMLCollection 객체에 변경 사항이 실시간으로 반영됩니다.
예제와 함께 살펴보겠습니다.
<!DOCTYPE html> <head> <style> .sold-out { color: orange; text-decoration: line-through; } </style> <title>HTMLCollection 예제</title> </head> <body> <h4>붕어빵 메뉴 리스트</h4> <ul> <li class="menu">팥</li> <li class="menu">슈크림</li> <li class="menu">피자</li> </ul> <script> // class 값이 'menu'인 요소 노드를 모두 찾아 HTMLCollection 객체에 담아 반환합니다. const htmlcoll = document.getElementsByClassName('menu'); console.log(htmlcoll); // HTMLCollection(3) [li.menu, li.menu, li.menu] // HTMLCollection 객체 내 모든 요소의 class 값을 'sold-out'으로 변경합니다. for (let i = 0; i < htmlcoll.length; i++) { htmlcoll[i].className = 'sold-out'; } </script> </body> </html>
위 예제는 getElementsByClassName() 메서드를 사용해 class 값이 ‘menu’인 요소 노드를 모두 취득합니다. 그리고 취득한 요소들을 담고 있는 HTMLCollection 객체를 for문으로 순회하며 class 값을 변경합니다. 이 과정을 통해 다음의 결괏값을 기대할 수 있습니다.
  • HTMLCollection 객체에 담긴 모든 <li> 요소의 class 값이 ‘sold-out’으로 변경
  • class 값 ‘sold-out’에 미리 지정해 둔 스타일이 적용되어 모든 <li> 요소의 텍스트 컬러가 ‘orange’로 변경되고 취소선 추가
 
그러나 여기서 HTMLCollection의 재미있는 현상을 발견할 수 있습니다. 위 예제를 실행해보면 다음의 이미지처럼 두 번째 <li> 요소의 텍스트 ‘슈크림’이 여전히 검정색입니다.
notion imagenotion image
 
왜 두 번째 노드의 class 값이 변하지 않았을까요? 이는 HTMLCollection이 라이브 객체이기 때문입니다. 컬렉션 객체가 for문으로 순회하며 노드가 수정됨과 동시에 컬렉션 객체도 실시간으로 수정되었기 때문에 예기치 못한 결과가 발생했습니다.
 
위 예제의 for문 순회 과정을 따라가 보겠습니다.
  1. 첫 번째 순회
    1. htmlcoll[0]인 <li class="menu">팥</li> 노드의 class 값이 ‘sold-out’으로 변경됩니다. 이 노드는 class 값이 바뀌었기 때문에 getElementsByClassName('menu')에 속하지 않습니다. 따라서 htmlcoll 객체에서 실시간으로 삭제됩니다.
  1. 두 번째 순회
    1. htmlcoll[1]의 class 값이 변경될 차례입니다. 첫 번째 순회에서 첫 번째 요소가 htmlcoll 객체에서 삭제되었기 때문에 htmlcoll[1]이 <li class="menu">피자</li>로 바뀌었습니다. 따라서 이 노드의 class 값이 ‘sold-out’으로 변경되고 마찬가지로 htmlcoll 객체에서 삭제됩니다.
  1. 세 번째 순회
    1. htmlcoll[2]의 class 값이 변경될 차례입니다. i === 2이지만 두 번의 순회를 거치며 htmlcoll 객체에는 1개의 노드만 남아있어 htmlcoll.length === 1입니다. 따라서 for문의 조건식이 false로 평가되어 for문이 종료되고 <li class="menu">슈크림</li> 노드는 class 값이 변경되지 않은 채로 htmlcoll에 남습니다.
       
이처럼 HTMLCollection은 객체의 상태 변화가 실시간으로 업데이트 되는 라이브 객체이며 조작 시에 부작용이 발생할 수 있습니다. 이러한 부작용을 해결하기 위해 NodeList를 사용할 수 있습니다.
 

3.3.3. NodeList

NodeList는 querySelectorAll() 메서드가 반환하는 DOM 컬렉션 객체입니다. 이때 HTMLCollection 객체와 달리 NodeList는 정적 객체입니다.
위와 같은 예제에서 querySelectorAll() 메서드를 사용해 NodeList 객체를 조작해보겠습니다. script 태그의 내용을 아래의 코드로 수정하여 실행해보겠습니다.
<script> // class 값이 'menu'인 요소 노드를 모두 탐색하여 NodeList 객체에 담아 반환합니다. const nodelist = document.querySelectorAll('.menu'); // NodeList 객체 내 모든 요소의 class 값을 'sold-out'으로 변경합니다. for (let i = 0; i < nodelist.length; i++) { nodelist[i].className = 'sold-out'; } </script>
notion imagenotion image
NodeList 객체에서는 취득한 모든 노드의 class 값이 변경되었습니다. 여기서 정적 객체인 NodeList와 라이브 객체인 HTMLCollection의 차이점을 명확하게 확인할 수 있습니다.
 
또한 HTMLCollection과 달리 NodeList는 forEach() 메서드를 사용할 수 있습니다.
// forEach()를 사용해도 같은 결괏값을 얻을 수 있습니다. nodelist.forEach(function (i) { i.className = 'sold-out' });
 
단, NodeList의 forEach() 메서드는 IE에서 지원하지 않습니다.
notion imagenotion image
 
NodeList 객체를 조작할 때 유의해야 할 점은 Node.childNodes 프로퍼티가 반환하는 NodeList는 HTMLCollection과 같이 라이브 객체로 동작한다는 점입니다. 따라서 DOM 컬렉션 객체를 안전하게 조작하기 위해 Array.from()이나 전개 구문을 사용해 배열로 변환하는 것을 권장합니다.
 

4. 노드 탐색

4.1. 노드 탐색의 개념

4.1.1. 노드 탐색의 필요성

앞에서 보았던 것처럼 우리는 찾고자 하는 노드를 바로 취득할 수 있습니다. 그렇다면 왜 노드 탐색을 배우는 걸까요? 노드 탐색을 이용하면 document 내의 노드를 훨씬 빠르게 찾을 수 있어서 자바스크립트 최적화에 도움을 줍니다. 또한 인접한 노드를 찾을 때 매번 일일이 찾아야 한다면 번거로울 것입니다.
<div> <p>아래에 내용을 입력하세요</p> <input type="text" placeholder="입력하시오"> <button type="button">버튼입니다</button> </div> <script> // 루트 노드부터 노드를 탐색하는 경우 console.log(document.querySelector("p")); console.log(document.querySelector("input")); console.log(document.querySelector("button")); // 기준 노드를 취득하고 노드 탐색을 하는 경우 const myDiv = document.querySelector("div"); console.log(myDiv.children); console.log(myDiv.children[0]); </script>
               노드 탐색 없이 노드를 취득하는 경우               노드 탐색 없이 노드를 취득하는 경우
노드 탐색 없이 노드를 취득하는 경우
위 코드에서 루트 노드부터 노드를 탐색하는 경우에는 <div>의 자식 요소 노드들을 탐색하기 위해서 각 자식 요소마다 최상단 루트로부터 접근해야 합니다.
 
   기준 노드를 취득하고 노드 탐색을 하는 경우   기준 노드를 취득하고 노드 탐색을 하는 경우
기준 노드를 취득하고 노드 탐색을 하는 경우
기준 노드를 취득한 후, 취득한 노드로부터 탐색을 하면 최상단 루트로부터 탐색하는 것에 비해 <div>의 모든 자식 요소 노드에 더 빠르게 접근이 가능합니다. 또한 자식 요소 노드들이 NodeList로 반환되기 때문에 인덱스를 통해 각각의 자식 요소 노드에도 접근이 가능하여 편리합니다[15].
 

4.1.2. 공백 텍스트 노드

노드 탐색을 본격적으로 살펴보기에 앞서, 공백 텍스트 노드에 대한 간단한 이해가 필요합니다. 텍스트 노드라고 하면 당연히 문자나 숫자 등의 텍스트가 생성하는 것이라고 생각하셨을 것입니다. 하지만 HTML 요소 사이에 들어가는 모든 공백으로 이루어진 문자(띄어쓰기, 탭, 줄 바꿈 등) 또한 텍스트 노드를 생성합니다. 이것을 공백 텍스트 노드라고 합니다[16].
태그와 DOM 트리를 비교하며 살펴보겠습니다.
<!--마크업 작성시 h1 태그와 strong 태그 사이에 개행을 한 경우--> <h1> <strong>나는 떡볶이를 좋아해!</strong> </h1> <!--마크업 작성시 h1 태그와 strong 태그 사이에 개행하지 않은 경우--> <h1><strong>나는 떡볶이를 좋아해!</strong></h1>
h1 태그와 strong 태그 사이에 개행을 한 경우h1 태그와 strong 태그 사이에 개행을 한 경우
h1 태그와 strong 태그 사이에 개행을 한 경우
h1 태그와 strong 태그 사이에 개행하지 않은 경우h1 태그와 strong 태그 사이에 개행하지 않은 경우
h1 태그와 strong 태그 사이에 개행하지 않은 경우
위와 같이 태그와 태그 사이에 공백이 들어가면 공백 텍스트 노드가 생기는 것을 확인할 수 있습니다. 이것을 없애기 위해서는 모든 공백을 없애고 작성하면 되지만 가독성이 떨어지기 때문에 그렇게 쓰지 않습니다.
 

4.2. 노드 탐색 프로퍼티

이제 본격적으로 노드 탐색 프로퍼티에는 어떤 것이 있는지 예시와 함께 알아보겠습니다. 우리가 지금부터 보게 되는 노드 탐색 프로퍼티는 읽기 전용이기 때문에 참조만 가능하고 값의 변경이 불가합니다.

4.2.1 부모 노드 탐색

(1) parentNode
부모 노드 탐색 프로퍼티는 Node.parentNode 입니다. 다음 예시를 통해 직접 부모 노드를 탐색해보겠습니다.
 
<section> <h1>Lorem ipsum, dolor sit amet consectetur adipisicing elit!</h1> <ul id="lists"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> </ul> </section> <script> const lists = document.querySelector("#lists"); console.log(lists.parentNode); </script>
<ul>의 요소 노드에 Node.parentNode를 사용하면 다음과 같이 <section> 요소 노드가 선택된 것을 확인할 수 있습니다.
notion imagenotion image
 

4.2.2. 자식 노드 탐색

(1) childNodes, children
Node.childNode는 자식 노드를 모두 반환합니다. Element.children은 자식 노드 중에서 요소 노드만 모두 반환합니다.
<section> <h1>Lorem ipsum, dolor sit amet consectetur adipisicing elit!</h1> <ul id="lists"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> </ul> </section> <script> const lists = document.querySelector("#lists"); console.log(lists.childNodes); // NodeList(7) [text, li, text, li, text, li, text] console.log(lists.children); // HTMLCollection(3) [li, li, li] </script>
<ul> 요소 노드에 Node.childNodes를 사용하면 다음과 같이 모든 자식 노드가 선택된 것을 확인할 수 있습니다. 자식 노드는 NodeList 형식으로 반환되며 위 결과로 반환된 텍스트 노드는 개행으로 인해 생성된 공백 텍스트 노드입니다.
notion imagenotion image
 
반면 Element.children을 사용하면 다음과 같이 자식 노드 중에서도 요소 노드만 모두 반환하는 것을 확인할 수 있습니다.
notion imagenotion image
 
(2) firstChild, firstElementChild
Node.firstChild는 자식 노드의 첫 번째 노드를 반환합니다. Element.firstElementChild는 자식 노드의 첫 번째 요소 노드를 반환합니다.
<section> <h1>Lorem ipsum, dolor sit amet consectetur adipisicing elit!</h1> <ul id="lists"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> </ul> </section> <script> const lists = document.querySelector("#lists"); console.log(lists.firstChild); console.log(lists.firstElementChild); </script>
<ul> 요소에 Node.firstChild를 사용하면 다음과 같이 첫 번째 자식 노드인 공백 텍스트 노드를 반환하는 것을 확인할 수 있습니다.
notion imagenotion image
 
반면 Element.firstElementChild를 사용하면 다음과 같이 첫 번째 자식 요소 노드인 <li>요소 노드를 반환하는 것을 확인할 수 있습니다.
notion imagenotion image
 
(3) lastChild, lastElementChild
Node.lastChild는 자식 노드의 마지막 노드를 반환합니다. Element.lastElementChild는 자식 노드의 마지막 요소 노드를 반환합니다.
<section> <h1>Lorem ipsum, dolor sit amet consectetur adipisicing elit!</h1> <ul id="lists"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> </ul> </section> <script> const lists = document.querySelector("#lists"); console.log(lists.lastChild); console.log(lists.lastElementChild) </script>
<ul> 요소에 Node.lastChild를 사용하면 다음과 같이 마지막 자식 노드인 공백 텍스트 노드를 반환하는 것을 확인할 수 있습니다.
notion imagenotion image
 
반면 Element.lastElementChild를 사용하면 마지막 자식 요소 노드인 <li>요소 노드를 반환하는 것을 확인할 수 있습니다.
notion imagenotion image
 

4.2.3. 형제 노드 탐색

(1) previousSibling, previousElementSibling
Node.prevoiusSibling은 형제 노드 중에서 자신의 바로 이전 형제 노드를 반환합니다. Element.previousElementSibling은 형제 노드 중에서 자신의 바로 이전 형제 요소 노드를 반환합니다.
<section class="clothes"> <nav> <ul class="category"> <li id="top">상의</li> <li id="outer">아우터</li> <li id="bottom">하의</li> </ul> </nav> </section> <script> const outer = document.querySelector("#outer"); console.log(outer.previousSibling); console.log(outer.previousElementSibling); </script>
<ul> 요소에 Node.previousSibling을 사용하면 다음과 같이 바로 이전 형제 노드를 반환하는 것을 확인할 수 있습니다. 위 결과로 반환한 텍스트 노드는 개행으로 인해 생성된 공백 텍스트 노드입니다.
notion imagenotion image
 
반면 Element.previousElementSibling을 사용하면 바로 이전 형제 요소 노드를 반환하는 것을 확인할 수 있습니다.
notion imagenotion image
 
(2) nextSibling, nextElementSibling
Node.nextSibling은 형제 노드 중에서 자신의 바로 다음 형제 노드를 반환합니다.
<section class="clothes"> <nav> <ul class="category"> <li id="top">상의</li> <li id="outer">아우터</li> <li id="bottom">하의</li> </ul> </nav> </section> <script> const outer = document.querySelector("#outer"); console.log(outer.nextSibling); console.log(outer.nextElementSibling); </script>
<li>요소에 Node.nextSibling을 사용하면 다음과 같이 바로 다음 형제 노드를 반환하는 것을 확인할 수 있습니다. 위 결과로 반환한 텍스트 노드는 개행으로 인해 생성된 공백 텍스트 노드입니다.
notion imagenotion image
 
반면 Element.previousElementSibling을 사용하면 바로 다음 형제 요소 노드를 반환하는 것을 확인할 수 있습니다.
notion imagenotion image
 

5. 노드 조작

지금까지 DOM 트리에서 요소를 탐색하는 과정을 살펴보았습니다. 이번 장에서는 요소의 내용을 가져오거나 수정하는 방법을 알아보겠습니다.

5.1. DOM 조작

DOM 조작은 HTML 문서를 수정하거나 변경하기 위한 JavaScript DOM API의 상호 작용입니다. DOM을 조작함으로서 새로고침 없이 요소를 생성, 수정하거나 스타일을 지정 또는 삭제할 수 있습니다. 즉 새로운 노드를 생성하여 DOM에 추가하거나, 기존 노드를 삭제 또는 교체를 하게 되는데 이때 리플로우, 리페인트가 발생합니다[17].
 

5.2. 요소 노드의 텍스트 조작 (요소의 내용을 가져오기/수정하기)

5.2.1. nodeValue

nodeValue 프로퍼티는 텍스트 노드로 표현되는 텍스트 값을 반환합니다. 텍스트 노드가 아닌 노드(문서 노드 또는 요소 노드)인 경우 nodeValue 프로퍼티는 null을 반환합니다.
 
(1) nodeValue 프로퍼티를 이용하여 텍스트 노드에 접근하기
notion imagenotion image
<!DOCTYPE html> <html> <body> <h1 id="title"><strong>즐거운</strong>자바스크립트</h1> </body> </html>
 
위의 예제에서 <h1> 요소는 다음과 같은 내용을 가지고 있습니다.
  • id 어트리뷰트 값을 가지고 있는 어트리뷰트 노드
  • 첫 번째 자식 요소는 <strong> 요소를 표현하는 요소 노드이며, 이 요소의 자식 노드는 “즐거운”이라는 텍스트를 가진 텍스트 노드
  • 두 번째 자식 요소는 “자바스크립트”라는 텍스트를 가진 텍스트 노드
 
nodeValue 프로퍼티를 사용하여 반환되는 값을 살펴보겠습니다.
// 문서 노드의 nodeValue 프로퍼티를 참조할 경우 console.log(document.nodeValue); // null const $title = document.getElementById('title') // 요소 노드의 nodeValue 프로퍼티를 참조할 경우 console.log($title.nodeValue); // null // 텍스트 노드의 nodeValue 프로퍼티를 참조할 경우 console.log($title.childNodes[1].nodeValue); // 자바스크립트
이처럼 nodeValue 프로퍼티를 사용하기 위해서는 텍스트 노드를 포함하고 있는 상위 요소의 노드를 선택하는 것이 아닌 반드시 텍스트 노드를 선택해야 합니다.
 
(2) nodeValue 프로퍼티를 이용하여 텍스트 노드 변경하기
nodeValue 프로퍼티를 사용하여 요소의 값을 가져오는 과정을 살펴보았습니다. 이 과정을 참고하여 <strong> 요소의 “즐거운” 텍스트를 수정해 보도록 하겠습니다.
<!DOCTYPE html> <html> <body> <h1 id="title"><strong>즐거운</strong>자바스크립트</h1> </body> </html>
// firstChild 프로퍼티를 사용하여 <strong> 요소의 자식 노드를 탐색합니다. const $textNode = document.querySelector('#title strong').firstChild console.log($textNode.nodeValue) // 즐거운 // 값을 할당하여 텍스트 노드의 값을 변경합니다. $textNode.nodeValue = "신나고 재미있는" console.log($textNode.nodeValue) // 신나고 재미있는
 

5.2.2. textContent

textContent 프로퍼티는 요소 노드의 텍스트 노드와 모든 자손 노드의 텍스트를 모두 취득하거나 변경합니다. 이때 HTML 마크업은 무시되며 입력된 문자열을 그대로 넣습니다. 다시 말해 요소 노드의 콘텐츠 영역(시작 태그와 종료 태그 사이) 내의 내용을 변경할 수 있으며, 모든 마크업을 포함하여 요소 내용 전체가 변경됩니다.
<!DOCTYPE html> <html> <body> <body> <h1 id="title"><strong>즐거운</strong>자바스크립트</h1> </body> </html>
console.log(document.getElementById('title').textContent) // 즐거운자바스크립트 // h1 요소 노드의 텍스트를 탐색하여 문자열인 값을 할당 document.getElementById('title').textContent = "신나는 자바스크립트 ❤️" console.log(document.getElementById('title').textContent) // 신나는 자바스크립트 ❤️
 
할당 전할당 전
할당 전
 




할당 후



할당 후
할당 후
 
이렇게 textContent 프로퍼티에 문자열을 할당하면 탐색한 요소 노드의 모든 자식 노드가 제거되고 할당한 문자열이 텍스트로 추가됩니다. 이때 할당한 문자열에 HTML 마크업이 포함되어 있어도 문자열 그대로 텍스트로 취급됩니다.
document.getElementById('title').textContent = "<strong>신나는</strong> 자바스크립트"
notion imagenotion image
notion imagenotion image
notion imagenotion image
 

5.2.3. textContent와 유사한 innerText

innerText 프로퍼티를 사용하여 텍스트 내용을 가져올 수도 있습니다. 하지만 다음과 같은 세 가지 이유로 사용을 권장하지 않습니다.
 
(1) 브라우저 지원 문제
대부분의 브라우저 제조사들이 innerText 프로퍼티를 지원하지만, 파이어폭스 브라우저는 이 프로퍼티를 지원하지 않기 때문에 innerText를 사용하고자 한다면 파이어폭스에 대한 대응 방안이 있어야 합니다.
 
(2) CSS에 의해 숨겨진 내용은 표시되지 않는 문제
<!DOCTYPE html> <html> <head> <style> #title strong { visibility: hidden; } </style> </head> <body> <h1 id="title"><strong>즐거운</strong>자바스크립트</h1> <script> const $title = document.getElementById('title'); console.log($title.textContent) // 즐거운자바스크립트 console.log($title.innerText) // 자바스크립트 </script> </body> </html>
 
(3) 성능 문제
innerText 프로퍼티는 요소가 화면에 보여지는지 결정하기 위한 레이아웃 규칙을 고려하기 때문에, textContent 프로퍼티를 통해 내용을 가져오는 것보다 더 느리게 동작할 수 있습니다.
 

5.3. HTML 콘텐츠 추가/제거

DOM 트리에 콘텐츠를 추가하거나 제거하는 방법은 크게 두 가지가 있습니다. 첫 번째로 innerHTML 프로퍼티를 이용하는 방법, 두 번째로 DOM을 조작하는 방법입니다.
 

5.3.1. innerHTML

요소 노드는 모두 innerHTML 프로퍼티를 가지고 있습니다. 요소 노드의 콘텐츠 영역(시작 태그와 종료 태그 사이) 내의 모든 HTML 마크업을 문자열로 반환할 수 있으며 콘텐츠 조회 및 수정이 가능합니다.
 
(1) innerHTML로 요소 노드 수정하기
notion imagenotion image
<!DOCTYPE html> <html> <body> <ul id="language"> <li>HTML</li> <li>CSS</li> </ul> </body> </html>
 
notion imagenotion image
// 콘텐츠를 수정할 요소 선택 const $language = document.getElementById('language'); // 노드 추가 $language.innerHTML += '<li>JavaScript</li>'
notion imagenotion image
// 노드 교체 $language.innerHTML = '<li>I love JavaScript</li>'
 
notion imagenotion image
// 노드 삭제 $language.innerHTML = ''
 

5.3.2. DOM 직접 조작하기

DOM을 직접 조작하는 방식은 innerHTML 프로퍼티를 이용하는 것보다 안전하지만 더 많은 코드를 작성해야 하고 더 느리게 동작할 수 있습니다. 요소와 텍스트 노드를 생성하는 DOM 메서드를 이용하여 필요한 노드를 생성한 후 DOM 트리에 추가하거나 제거합니다[18].
 

5.3.2.1. 노드 생성/추가

DOM을 직접 조작해서 노드를 생성하고 추가하려면 다음과 같은 단계를 거치게 됩니다.
(1) 요소 노드 생성
(2) 텍스트 노드 생성
(3) 텍스트 노드를 요소 노드의 자식 요소로 추가
(4) 요소 노드를 DOM에 추가
 
(1) 요소 노드 생성 과정
메서드
의미
반환
document.createElement(’태그명’)
태그명 요소 생성
요소
아래 예시를 통해 요소를 생성하고 추가하는 과정을 살펴보겠습니다.
<!DOCTYPE html> <html> <body> <ul id="language"> <li>HTML</li> <li>CSS</li> </ul> </body> </html>
 
  • 생성한 요소 노드를 변수에 저장합니다.
  • createElement() 메서드를 이용하여 새로운 요소 노드를 생성합니다.
  • createElement() 메서드의 매개변수에 태그 이름의 문자열을 인수로 전달합니다.
 
메서드를 통해 요소 노드를 생성했지만 아직 DOM 트리에 추가된 상태는 아닙니다. (4)단계까지 진행하기 전에는 DOM 트리에 추가되지 않습니다. 또한 createElement() 메서드로 생성한 요소 노드는 아무런 자식 요소를 가지고 있지 않습니다.
 
// 요소 노드를 추가할 상위 요소 노드를 변수에 저장합니다. const $ulElement = document.getElementById('language'); // 생성한 요소 노드를 변수에 저장합니다. const $liElement = document.createElement('li');
notion imagenotion image
 
(2) 텍스트 노드 생성 과정
메서드
의미
반환
document.createTextNode(’데이터’)
텍스트 노드 생성
텍스트 노드
  • createTextNode() 메서드를 이용하여 새로운 텍스트 노드를 생성합니다.
  • createTextNode() 메서드의 매개변수에 텍스트 노드 값으로 사용할 문자열을 인수로 전달합니다.
 
텍스트 노드는 요소 노드의 자식으로 존재해야 합니다. createTextNode() 메서드로 텍스트 노드를 생성했지만 요소 노드에 추가되지 않은 상태입니다.
 
// 생성한 텍스트 노드를 변수에 저장합니다. const textNode = document.createTextNode('JavaScript') // 텍스트 노드가 요소 노드에 추가되지 않은 상태이기 때문에 null을 반환합니다. console.log(textNode.parentNode) // null
notion imagenotion image
 
 
(3) 텍스트 노드를 요소 노드의 자식 노드로 추가하는 과정
메서드
의미
반환
부모 노드.appendChild(자식 노드)
부모 노드에 자식 노드 추가
자식 노드
  • appendChild() 메서드를 이용하여 요소 노드에 추가되지 않은 텍스트 노드를 마지막 자식 노드로 추가합니다.
  • appendChild() 메서드의 매개변수에 createTextNode() 메서드로 생성한 텍스트 노드를 인수로 전달합니다.
  • appendChild() 메서드를 호출한 요소 노드에 인수로 전달받은 텍스트 노드가 마지막 자식 노드로 추가됩니다.
// 텍스트 노드를 $liElement 요소 노드의 자식 노드로 추가합니다. $liElement.appendChild(textNode) // $liElement 요소 노드에 텍스트 노드가 추가된 것을 확인할 수 있습니다. console.log($liElement.childNodes) // NodeList [text]
notion imagenotion image
 
(4) 요소 노드를 DOM에 추가하는 과정
  • appendChild() 메서드를 사용하여 부모 노드에 마지막 자식 요소로 노드를 추가합니다.
 
기존의 DOM에 요소 노드를 추가하는 처리는 이 과정뿐이며, 이때 리플로우와 리페인트가 실행됩니다.
 
// $liElement 요소 노드를 기존 DOM 트리에 있던 $ulElement 요소의 마지막 자식 노드로 추가합니다. $ulElement.appendChild($liElement)
notion imagenotion image
notion imagenotion image
 
 

5.3.2.2. 노드 삽입 및 이동

메서드
의미
반환
부모 노드.insertBefore(삽입할 노드, 희망 위치)
부모 노드 안에 노드 추가
추가된 노드
insertBefore() 메서드는 첫 번째 인수로 전달 받은 노드를 두 번째 인수로 전달받은 노드 앞에 삽입합니다. 두 번째 매개변수가 null이면 appendChild()처럼 동작하며, 두 번째 매개변수가 없으면 에러가 발생합니다. DOM에 이미 존재하는 노드에 insertBefore() 메서드를 사용하면 현재 위치에서 노드를 제거하고 새로운 위치에 노드를 추가합니다.
 
아래 예시를 통해 살펴보겠습니다.
<!DOCTYPE html> <html> <body> <ul id="order"> <li>Second</li> <li>Third</li> </ul> </body> </html>
const $ulElement = document.getElementById('order'); // li 요소 노드와 텍스트 노드를 생성하고 연결합니다. const $liElement = document.createElement('li'); const textNode = document.createTextNode('First'); $liElement.appendChild(textNode); // 현재 $ulElement 노드의 자식 노드 리스트입니다. 주의 깊게 봐주시기 바랍니다. console.log($ulElement.childNodes) // NodeList(5) [text, li, text, li, text]
 
(1) firstChild
notion imagenotion image
// 생성한 $liElement 노드를 $ulElement 첫 번째 자식 노드 앞에 삽입합니다. $ulElement.insertBefore($liElement, $ulElement.firstChild); console.log($ulElement.childNodes) // NodeList(6) [li, text, li, text, li, text]
 
(2) lastChild
notion imagenotion image
// 생성한 $liElement 노드를 $ulElement 마지막 자식 노드 앞에 삽입합니다. $ulElement.insertBefore($liElement, $ulElement.lastChild); console.log($ulElement.childNodes); // NodeList(6) [text, li, text, li, li, text]
 
(3) null
notion imagenotion image
// 두 번째 매개변수가 null일 경우 appendChild()처럼 동작합니다. $ulElement.insertBefore($liElement, null); console.log($ulElement.childNodes); // NodeList(6) [text, li, text, li, text, li]
 
(4) 매개변수가 없음
// 두 번째 매개변수가 주어지지 않은 경우 $ulElement.insertBefore($liElement); // Uncaught TypeError: Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 1 present.
 
 

5.3.2.3. 노드 복제

메서드
의미
반환
노드.cloneNode(true/false)
노드 복제
복제된 노드
  • cloneNode() 메서드를 사용하여 단일 노드 혹은 노드 및 모든 자식 노드를 복제할 수 있습니다.
  • cloneNode() 메서드 매개변수에 true가 전달되지 않으면 텍스트 노드 없이 복제되고, true가 전달되면 모든 자손 노드를 포함하여 복제합니다.
 
아래 예제를 살펴보겠습니다.
<!DOCTYPE html> <html> <body> <ul id="language"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> </ul> </body> </html>
 
notion imagenotion image
// 복제할 요소를 변수에 저장합니다. const $ulElement = document.getElementById('language'); // cloneNode() 메서드에 true 인자를 전달한 경우 const cloneUl = $ulElement.cloneNode(true) console.log(cloneUl.innerHTML) // <li>HTML</li> // <li>CSS</li> // <li>JavaScript</li> // $ulElement 요소 노드에 복제한 노드를 추가하였습니다. $ulElement.appendChild(cloneUl);
 
notion imagenotion image
// 복제할 요소를 변수에 저장합니다. const $ulElement = document.getElementById('language'); // cloneNode() 메서드에 false 인자를 전달한 경우 const cloneUl = $ulElement.cloneNode(false) console.log(cloneUl.innerHTML) // 비어있음 (복제된 요소 노드만 존재합니다.)
 

5.3.2.4. 노드 교체 및 제거

 
  • 노드 교체
메서드
의미
반환
부모 노드.replaceChild(새 노드, 교체할 자식 노드)
부모 노드의 자식 노드를 교체
없음
replaceChild() 메서드를 호출한 노드의 자식 노드를 새로운 노드로 교체합니다. 첫 번째 매개변수로 교체할 새로운 노드를 인수로 전달하고, 두 번째 매개변수로 이미 존재하고 있는, 교체될 노드를 인수로 전달합니다.
 
아래 예제에서 텍스트 노드가 “Java”로 되어있는 <li> 요소를 “JavaScript”로 변경해 보겠습니다.
<!DOCTYPE html> <html> <body> <ul id="language"> <li>HTML</li> <li>CSS</li> <li class="change">Java</li> </ul> </body> </html>
// replaceChild() 메서드를 호출할 노드 const $language = document.getElementById('language') // 기존 노드 (교체될 노드) const changeElem = document.querySelector('.change') // 교체할 요소 노드 생성 const newElem = document.createElement('li') const textNode = document.createTextNode('JavaScript') newElem.appendChild(textNode) newElem.textContent = "JavaScript" // $language 요소 노드의 자식 노드 중 changeElem 요소 노드를 newElem 요소 노드로 변경 $language.replaceChild(newElem, changeElem)
 
  • 노드 제거
메서드
의미
반환
부모 노드.removeChild(제거할 자식 노드)
부모 요소에서 자식 요소 제거
제거된 노드
노드를 제거하려면 다음과 같은 단계를 거치게 됩니다.
(1) 제거할 요소 노드를 변수에 저장
(2) 제거할 요소를 포함한 부모 요소 노드를 변수에 저장
(3) 앞에서 선택한 부모 요소 노드에서 제거할 요소 노드 제거
 
아래 예제에서 텍스트 노드가 “Java”로 되어있는 <li> 요소를 제거해보겠습니다.
<!DOCTYPE html> <html> <body> <ul id="language"> <li>HTML</li> <li>CSS</li> <li class="remove">Java</li> <li>JavaScript</li> </ul> </body> </html>
// removeChild() 메서드를 호출할 노드 const $language = document.getElementById('language') // 제거할 노드 const removeElem = document.querySelector('.remove') // $language 요소 노드의 자식 노드 중 removeElem 요소 노드를 제거 $language.removeChild(removeElem)
 
 
 

Reference

1. 노드에 관한 설명
 
2. DOM 트리
 
3. 노드 취득 / 컬렉션 객체
 
4. 노드 탐색
 
5. 노드 조작