1. 개요

- 하나의 HTML 문서는 <html></html>을 최상위 태그로 하여 안에 여러 태그 요소와 문자열로 구성된다. HTML 문서의 태그 요소들은 모두 각자의 부모 태그 안에 자식 태그로서 들어가 있어 마치 트리와 같은 구조를 갖는데, 브라우저는 HTML 문서를 파싱한 후 이들 태그 요소들을 각각 하나의 객체로 보는 트리를 만든 후 이 트리를 기준으로 페이지를 렌더링 한다. 이처럼 여러 태그 요소들로 형성된 트리 구조의 model을 DOM(document object model)이라 부른다. 

2. HTML과 DOM의 차이

- 트리 구조가 아닌 HTML 문서_(이러한 HTML 문서는 정상 작동하지 않는다)_는 DOM 트리를 형성하지 않는다.

- HTML 문서 내부에 작성된 자바스크립트에 의해 DOM 객체들이 새로 추가/삭제되어, HTML 문서상으로 표현된 것보다 더 많거나 적은 DOM 객체를 가진 DOM 트리가 되는 경우가 있다. 

3. DOM 트리의 노드의 종류, 속성

1) 노드의 종류

- element node: 태그 요소 노드를 element node라 하며, 다른 요소 노드의 자식 노드이자 또 다른 요소 노드, 속성 노드, 텍스트 노드의 부모 노드로서 존재한다.

- attribute node: 요소 노드의 자식 노드로서 존재하며, 부모 태그가 갖는 속성값을 저장한다. 추가로 자식을 갖지 못한다.

- text node: 요소 노드의 자식 노드로서 존재하며, 부모 태그 안에 들어있는 텍스트값을 저장한다. 추가로 자식을 갖지 못한다.

2) 노드 객체가 갖는 속성

- 노드 객체는 nodeType, nodeName, nodeValue 세 개의 속성을 가진다.

  nodeType nodeName nodeValue
element node 1 태그명이 저장된다. null이 저장되며 값이 변경되지 않는다.
attribute node 2 속성명이 저장된다. 속성값이 저장된다.
text node 3 ‘#text’가 저장되며 값이 변경되지 않는다. 문자열이 저장된다.

4. DOM 트리 동적 구성 방법

1) 노드의 생성 및 추가

  노드 생성 방법 DOM 트리 추가 방법
element node const 노드1 = document.createElement(“태그 이름”); 부모노드.appendChild(노드1);
부모노드.insertBefore(노드1, 부모노드의 다른 자식노드);
attribute node const 노드1 = document.createAttribute(“속성명”);
부모노드.setAttribute(“속성명”, “속성값”);
부모노드.속성명 = “속성값”;
부모노드.setAttributeNode(노드1);
text node const 노드1 = document.createTextNode(“문자열”); 부모노드.appendChild(노드1);

2) DOM 트리에서 노드 삭제

1
2
부모노드.removeChild(노드1);
부모노드.removeAttribute("속성명");

3) 특정 노드를 다른 노드로 대체

부모노드.replaceChild(노드1, 노드2);

- 부모노드의 자식노드인 노드2를 DOM 트리에서 제거하고 그 위치에 노드1을 삽입한다.

5. 각 노드의 부모/자식/형제 호출

1) 부모노드 호출

- 특정 노드의 부모노드는 노드이름.parentNode 또는 노드이름.parentElement로 호출할 수 있다.

- 자식노드를 갖는 것은 루트노드를 제외하면 요소노드뿐이므로 이 둘은 거의 항상 같은 결과를 내지만 예를 들어 document.documentElement 노드의 경우 .parentNode와 .parentElement의 값이 서로 다르다. document.documentElement 노드의 부모노드는 document 노드인데, 이는 DOM 트리의 루트노드로 요소노드가 아니기 때문이다. (document.documentElement.parentElement는 null을 리턴한다.)

2) 자식의 유무를 확인할 수 있는 메서드

1
2
3
4
5
6
7
8
if (부모노드.hasChildNodes())
    console.log(`It has child nodes.`);

if (부모노드.hasAttributes())
    console.log(`It has child nodes.`);
    
if (부모노드.hasAttribute("속성명"))
    console.log(`It has 속성명 attribute.`);

3) 첫 번째 자식과 마지막 자식, 자식들이 저장된 객체, 형제노드

  노드 타입 무관 요소노드
첫 번째 자식 부모노드.firstChild 부모노드.firstElementChild
마지막 자식 부모노드.lastChild 부모노드.lastElementChild
자식 노드들이 배열처럼 저장된 객체
(배열은 아니지만 iterable하다.)
부모노드.childNodes 부모노드.children
바로 앞 형제노드 노드1.previousSibling 노드1.previousElementSibling
바로 뒤 형제노드 노드1.nextSibling 노드1.nextElementSibling

- 요소 노드의 자식 속성의 속성값: 요소노드.getAttribute(속성명)

4) 자손 노드를 쿼리로 호출

(1) 노드1.querySelector(“선택자”)

- “선택자”에 쿼리를 넣으면 노드1의 자손 노드 중 해당 쿼리에 해당하는 첫 번째 자손 노드를 리턴한다.

- 선택자에는 태그 이름, 클래스 이름, id를 넣을 수 있으며 이들을 조합해 쿼리를 만들어 넣을 수도 있다. 각각은 띄어쓰기로 구분하며, 왼쪽 태그/클래스/id의 자식 중 오른쪽 태그/클래스/id를 선택하도록 하는 쿼리를 만든다. 쉼표(,)로 구분하는 경우에는 각 쿼리가 서로 독립적인 것으로 본다.

- 태그 이름/클래스 이름/id 옆에 []를 바로 붙여 쓰는 쿼리도 있다. 이는 조건문으로, 전체 해당하는 노드 중 [] 안의 조건문을 만족하는 노드를 리턴한다. 조건문은 ‘속성명=속성값’의 형식으로 이루어진다. 다음은 MDN에서 참조한 선택자 쿼리의 한 예이다.

var el = document.querySelector("div.user-panel.main input[name=login]");

(2) 노드1.querySelectorAll(“선택자”)

- “선택자” 쿼리에 해당하는 노드1의 모든 자손 노드들이 array-like 객체에 담겨 리턴된다.