DOM 이란,
HTML 문서의 계층적 구조와 정보를 표현하며 이를 제어할 수 있는 API, 즉 프로퍼티와 메서드를 제공하는 트리 자료구조다.
자세히 알아보자면,
문서 객체 모델을 줄인 DOM은 문서의 구조화된 표현(structered representation)을 제공하며 프로그래밍 언어가 DOM 구조에 접근 할 수 있는 방법을 제공하며 그들이 문서 구조, 스타일, 내용 등을 변경할 수 있게 돕는다.
1 . 1 HTML 요소와 노드 객체
HTML요소는 HTML 문서를 구성하는 개별적인 요소를 의미한다.
HTML 요소는 렌더링 엔진에 의해 파싱되어 DOM을 구성하는 요소 노드 객체로 변환된다.
이 때, HTML 요소의 어트리뷰트는 어트리뷰트 노드로, HTML요소의 텍스트컨텐츠는 텍스트 노드로 변환된다.
트리 자료구조
트리 자료구조는 노드들의 계층 구조로 이루어진다. 즉, 트리 자료구조는 부모 노드와 자식 노드로 구성되어 노드 간의 계층적 구조를 표현하는 비선형 자료구조를 말한다. 트리 자료 구조는 하나의 최상위 노드에서 시작.
최상위 노드는 루트 노드라고 하고, 0개 이상의 자식 노드를 갖는다.
자식 노드가 없는 노드를 리프 노드 라고한다.
1 . 2 노드 객체의 타입
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="style.css" />
<title>Document</title>
</head>
<body>
<ul>
<li id="apple">Apple</li>
<li id="banana">Banana</li>
<li id="orange">Orange</li>
</ul>
<script src="app.js"></script>
</body>
</html>
예로 위의 HTML 문서를 렌더링 엔진이 파싱한다고 생각해보자.
렌더링 엔진은 위의 HTML 문서를 파싱하여 아래와 같이 DOM을 생성한다.
노드 객체는 종류가 있고 상속 구조를 갖는다. 총 12개 종류의 노드 타입이 있고, 이 중에서 중요한 노드 타입은 4가지 이다.
(1) 문서노드
문서노드는 DOM 트리의 최상위에 존재하는 루트 노드로서 document 객체를 가리킨다. document 객체는 브라우저가 렌더링한 HTML 문서 전체를 가리키는 객체로서 전역 객체의 프로퍼티에 바인딩 되어있다. 따라서 문서 노드는 window.document 또는 document로 참조 가능.
(2) 요소 노드
요소 노드는 HTML 요소를 가리키는 객체이다. 요소 노드는 HTML 요소 간의 중첩에 의해 부자 관계를 가지며, 이 부자 관계를 통해 정보를 구조화한다. 따라서 요소 노드는 문서의 구조를 표현한다고 할 수 있음.
(3) 어트리뷰트 노드
어트리뷰트 노드는 HTML 요소의 어트리뷰트를 가리키는 객체다. 어트리뷰트 노드는 어트리뷰트가 지정된 HTML 요소의 요소 노드와 연결되어 있음. 단, 요소 노드는 부모 노드와 연결되어 있지만 어트리뷰트 노드는 부모 노드와 연결되어 있지 않고 요소 노드에만 연결되어 있다.
따라서, 어트리뷰트 노드에 접근하여 어트리뷰트를 참조하거나 변경하려면 먼저 요소 노드에 접근해야 한다.
(4) 텍스트 노드
텍스트 노드는 HTML 요소의 텍스트를 가리키는 객체다. 요소 노드가 문서의 구조를 표현한다면, 텍스트 노드는 문서의 정보를 표현한다고 할 수 있다. 텍스트 노드는 요소 노드의 자식 노드이며, 자식 노드를 가질 수 없는 리프 노드이다. 즉, 텍스트 노드는 DOM트리의 최종단.
2 . 요소 노드 취득
HTML 구조나 내용 또는 스타일 등을 동적으로 조작하려면 먼저 요소 노드를 취득해야 한다. 즉, 요소 노드의 취득은 HTML 요소를 조작하는 시작점이다
2 . 1 id를 이용한 요소 노드 취득
Document.prototype.getElementById 메서드는 인수로 전달한 id 어트리뷰트 값을 갖는 하나의 요소 노드를 탐색하여 반환한다.
<body>
<div id="bar"></div>
</body>
<script>
document.getElementById('bar')
</script>
id 값은 HTML 문서 내에서 유일한 값이어야 함.
단, HTML 문서 내에 중복된 id 값을 갖는 HTML 요소가 여러개 존재하더라도 에러발생 X
중복된 id 값을 갖는 요소가 여러개 존재할 경우, 인수로 전달된 id 값을 갖는 첫번째 요소 노드만 반환.
만약 인수로 전달된 id 값을 갖는 HTML 요소가 없을 경우 null 반환.
<body>
<div id="bar"></div>
</body>
<script>
console.log(bar);
// <div id="bar"></div>
</script>
2 . 2 태그 이름을 이용한 요소 노드 취득
Document.prototype.getElementsByTagName 메서드는 인수로 전달한 태그 이름을 갖는 모든 요소 노드들을 탐색하여 반환한다.
이 메서드는 여러 개의 요소 노드 객체를 갖는 DOM 컬렉션 객체인 HTMLCollection 객체를 반환.
<body>
<ul>
<li id="apple">Apple</li>
<li id="banana">Banana</li>
<li id="orange">Orange</li>
</ul>
<script>
// 태그 이름이 li인 요소 노드를 모두 탐색하여 반환
// 탐색된 요소 노드들은 HTMLCollection 객체에 담겨 반환
// HTMLCollection 객체는 유사 배열 객체이면서 이터러블.
const $elems = document.getElementsByTagName("li");
// 취득한 모든 요소 노드의 style.color 프로퍼티 값을 변경.
// HTMLCollection 객체를 배열로 변환하여 순회하며 color 프로퍼티 값을 변경
[...$elems].forEach((elem) => {
elem.style.color = "red";
});
</script>
</body>
함수는 하나의 값만 반환할 수 있으므로 여러 개의 값을 반환하려면 배열이나 객체와 같은 자료구조에 담아 반환해야함.
2 . 3 class를 이용한 요소 노드 취득
document.prototype.getElementsByClassName 메서드는 인수로 전달한 class 어트리뷰트 값을 갖는 모든 요소 노드들을 탐색하여 반환.
getElementsByTagName 메서드와 마찬가지로 getElementsByClassName 메서드는 여러 개의 요소 노드 객체를 갖는 DOM 컬렉션 객체인 HTMLCollection객체를 반환
<body>
<ul>
<li class="fruit apple">Apple</li>
<li class="fruit banana">Banana</li>
<li class="fruit orange">Orange</li>
</ul>
<script>
// class 값이 'fruit'인 요소 노드를 모두 탐색하여 HTMLCollection 객체에 담아 반환
const $elems = document.getElementsByClassName("fruit");
// 취득한 모든 요소의 CSS color 프로퍼티 값을 변경한다.
[...$elems].forEach((elem) => {
elem.style.color = "red";
});
// class 값이 'fruit apple'인 요소 노드를 모두 탐색하여 HTMLCollection 객체에 담아 반환
const $apples = document.getElementsByClassName("fruit apple");
// 취득한 모든 요소 노드의 style.color 프로퍼티 값을 변경
[...$apples].forEach((elem) => {
elem.style.color = "blue";
});
</script>
</body>
2 . 4 CSS 선택자를 이용한 요소 노드 취득
CSS 선택자는 스타일을 적용하고자 하는 HTML 요소를 특정할 때 사용하는 문법
- 인수로 전달한 CSS 선택자를 만족시키는 요소 노드가 여러 개인 경우 첫번째 요소 노드만 반환
- 인수로 전달된 CSS 선택자를 만족시키는 요소 노드가 존재하지 않는 경우 null을 반환
- 인수로 전달한 CSS 선택자가 문법에 맞지 않는 경우 DOMException 에러가 발생
<body>
<ul>
<li class="apple">Apple</li>
<li class="banana">Banana</li>
<li class="orange">Orange</li>
</ul>
<script>
// class 어트리뷰트 값이 'banana'인 첫번째 요소 노드를 탐색하여 반환.
const $elem = document.querySelector(".banana");
// 취득한 요소 노드의 style.color 프로퍼티 값을 변경
$elem.style.color = "red";
</script>
</body>
<body>
<ul>
<li class="apple">Apple</li>
<li class="banana">Banana</li>
<li class="orange">Orange</li>
</ul>
<script>
// ul 요소의 자식 요소인 li 요소를 모두 탐색하여 반환.
const $elems = document.querySelectorAll("ul > li");
// 취득한 요소 노드들은 NodeList 객체에 담겨 반환
console.log($elems); // NodeList(3) [li.apple, li.banana, li.orange]
// 취득한 모든 요소 노드의 style.color 프로퍼티 값을 변경.
// NodeList는 forEach 메서드를 제공함
$elems.forEach((elem) => {
elem.style.color = "red";
});
</script>
</body>
document.prototype.querySelectorAll 메서드는 인수로 전달한 CSS 선택자를 만족시키는 모든 요소 노드를 탐색하여 반환.
여러개의 요소 노드 객체를 갖는 DOM 컬렉션 객체인 NodeList 객체를 반환. NodeList 객체는 유사 배열 객체이면서 이터러블.
- 인수로 전달된 CSS 선택자를 만족시키는 요소가 존재하지 않는 경우 빈 NodeList 객체를 반환
- 인수로 전달한 CSS 선택자가 문법에 맞지 않는 경우 DOMException 에러가 발생.
CSS 선택자 문법을 사용하는 querySelector, querySelectorAll 메서드는 getElementById, getElementsBy*** 의 메서드보다 조금 느린 것으로 알려져 있다. 하지만 CSS 선택자 문법을 사용하여 구체적인 조건으로 요소 노드를 취득, 일관된 방식으로 요소 노드를 취득의 장점이 있음.
그래서 CSS 선택자 문법은 querySelector, querySelectorAll 을 많이많이 써라~~~