HTML 문서의 계층적 구조와 정보를 표현하며 이를 제어할 수 있는 API, 즉 프로퍼티와 메서드를 제공하는 트리 자료구조.
HTML 요소는 HTML 문서를 구성하는 개별적인 요소를 의미한다.
HTML 요소는 렌더링 엔진에 의해 파싱되어 DOM을 구성하는 요소 노드 객체로 변환된다.
이때 HTML의 어트리뷰트는 어트리뷰트 노드로, 텍스트 콘텐츠는 텍스트 노드로 변환.
HTML 요소의 콘텐츠 영역에는 텍스트뿐만 아니라 다른 HTML 요소도 포함할 수 있다.
이때 HTML 요소 간에 중첩 관계에 의한 계층적인 부자 관계가 형성되어 HTML 요소들을 객체화한 모든 노드 객체들을 트리 자료 구조로 구성한다.
노드 객체는 총 12개의 종류가 있고 그 중에서 중요한 노드 타입은 다음과 같이 4가지다.
-
문서 노드(document node)
DOM 트리의 최상위에 존재하는 루트 노드로서 domcument 객체를 가리킨다.
모든 자바스크립트 코드는 전역 객체 window의 document 프로퍼티에 바인딩되어 있는 하나의 document 객체를 바라본다.
문서 노드는 DOM 트리의 루트 노드이므로 DOM 트리의 노드들에 접근하기 위한 진입점 역할을 담당한다. -
요소 노드(element node)
HTML 요소를 가리키는 객체이다.
HTML 요소 간의 중첩에 의해 부자 관계를 가지며, 이 부자 관계를 통해 정보를 구조화한다. 즉 문서의 구조를 표현한다. -
어트리뷰트 노드(attribute node)
HTML 요소의 어트리뷰트를 가리키는 객체로 어트리뷰트가 지정된 HTML 요소의 요소 노드와 연결되어 있다.
어트리뷰트 노드는 요소 노드와 달리 부모 노드가 없으므로 요소 노드의 형제 노드는 아니다. 따라서 어트리뷰트 노드에 접근하려면 먼저 요소 노드에 접근해야 한다. -
텍스트 노드(text node)
HTML 요소의 텍스트를 가리키는 객체로 문서의 정보를 표현한다.
요소 노드의 자식 노드이며, 자식 노드를 가질 수 없는 리프 노드이다.
DOM은 HTML 문서의 계층적 구조와 정보를 표현하는 것은 물론 노드 객체의 종류, 즉 노드 타입에 따라 필요한 기능을 프로퍼티와 메서드의 집합인 DOM API로 제공한다.
이 DOM API를 통해 HTML의 구조나 내용 또는 스타일 등을 동적으로 조작할 수 있다.
HTML의 구조나 내용 또는 스타일 등을 동적으로 조작하려면 먼저 요소 노드를 취득해야 한다.
Document.prototype.getElementById 메서드는 인수로 전달한 id 어트리뷰트 값을 갖는 하나의 요소 노드를 탐색하여 반환한다.
<!DOCTYPE html>
<html>
<body>
<ul>
<li id="apple">Apple</li>
<li id="banana">Banana</li>
<li id="orange">Orange</li>
<ul>
<script>
// id 값이 'banana'인 요소 노드를 탐색하여 반환.
// 두 번째 li 요소가 파싱되어 생성된 요소 노드가 반환.
const $elem = document.getElementById('banana');
// 취득한 요소 노드의 style.color 프로퍼티 값을 변경.
$elem.style.color = 'red';
</script>
</body>
</html>
id 값은 HTML 문서 내에서 유일한 값이어야 하며, class 어트리뷰트와는 달리 공백 문자로 구분하여 여러 개의 값을 가질 수 없다.
단, HTML 문서 내에 중복된 id 값을 갖는 요소가 여러 개 존재하더라도 에러가 발생하지 않고 해당 id 값을 갖는 첫 번째 요소 노드만 반환한다.
인수로 전달된 id 값을 갖는 HTML 요소가 존재하지 않는 경우 getElementById 메서드는 null을 반환한다.
HTML 요소에 id 어트리뷰트를 부여하면 id 값과 동일한 이름의 전역 변수가 암묵적으로 선언되고 해당 노드 객체가 할당되는 부수효과가 있다.
<!DOCTYPE html>
<html>
<body>
<div id="foo"></div>
<script>
// id 값과 동일한 이름의 전역 변수가 암묵적으로 선언되고 해당 노드 객체가 할당된다.
console.log(foo === document.getElementById('foo')); // true
// 암묵적 전역으로 생성된 전역 프로퍼티는 삭제되지만 전역 변수는 삭제되지 않는다.
delete foo;
console.log(foo); // <div id="foo"></div>
</script>
</body>
</html>
단, id 값과 동일한 이름의 전역 변수가 이미 선언되어 있으면 이 전역 변수에 노드 객체가 재할당되지 않는다.
Document.prototype/Elelment.prototype.getElementsByTagName 메서드는 인수로 전달한 태그 이름을 갖는 모든 요소 노드들을 탐색하여 반환한다.
여러 개의 요소 노드 객체를 갖는 DOM 컬렉션 객체인 HTMLCollection 객체를 반환한다.
<!DOCTYPE html>
<html>
<body>
<ul>
<li id="apple">Apple</li>
<li id="banana">Banana</li>
<li id="orange">Orange</li>
</ul>
<script>
// 태그 이름이 li인 요소 노드를 모두 탐색하여 반환한다.
// 탐색된 요소 노드들은 HTMLCollection 객체에 담겨 반환된다.
const $elems = document.getElementsByTagName('li');
// HTMLCollection 객체를 배열로 변환하여 순회하며 color 프로퍼티 값을 변경.
[...$elems].forEach(elem => { elem.style.color = 'red'; });
</script>
</body>
</html>
HTML 문서의 모든 요소 노드를 취득하려면 인수로 *를 전달한다.
Document.prototype.getElementsByTagName 메서드는 DOM의 루트 노드인 문서 노드 document를 통해 호출하며 DOM 전체에서 요소 노드를 탐색하여 반환한다.
Element.prototype.getElementsByTagName 메서드는 특정 요소 노드를 통해 호출하며, 특정 요소 노드의 자손 노드 중에서 요소 노드를 탐색하여 반환한다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
<li>Orange</li>
</ul>
<ul>
<li>HTML</li>
</ul>
<script>
// DOM 전체 내에서 태그 이름이 li인 요소 노드를 모두 탐색하여 반환한다.
const $lisFromDocument = document.getElementsByTagName('li');
console.log($lisFromDocument); // HTMLCollection(4) [li, li, li, li]
// ul#fruits 요소의 자손 노드 중에서 태그 이름이 li인 요소 노드를 모두 탐색하여 반환한다.
const $fruits = document.getElementById('fruits');
const $lisFromFruits = $fruits.getElementsByTagName('li');
console.log($lisFromFruits); // HTMLCollection(3) [li, li, li]
</script>
</body>
</html>
만약 인수로 전달된 태그 이름을 갖는 요소가 존재하지 않는 경우 빈 HTMLCollection 객체를 반환한다.
