[모던 자바스크립트 Deep Dive] 39. DOM
39. DOM
DOM은 HTML 문서의 계층적 구조와 정보를 표현하며 이를 제어할 수 있는 API 즉 프로퍼티와 메서드를 제공하는 트리 자료구조이다.
39.1 노드
- HTML 요소 간의 부자 관계를 반영하여 HTML 문서의 구성요소인 HTML 요소를 객체화한 모든 노드 객체들을 트리자료로 구성한다
1. 트리 자료구조
- 노드들의 계층 구조로 이뤄진다. 트리 자료구조는 부모노트와 자식노드로 구성되어 노드간의 계층적 구조를 표현하는 비선형 자료구조를 말한다.
- 노드 객체들로 구성된 트리 자료구조를 DOM 이라고 한다.
- 노드 객체의 트리로 구조화되어 있기 때문에 DOM 트리라고 부르기도 한다
2. 노드 객체의 타입
- 문서노드 DOM 트리 최상위 루트 노드
- 요소 노트 HTML 요소를 가리킨다
- 어트리뷰트 노드 HTML 요소의 어트리뷰트를 가리키는 객체다,,\
- 텍스트 노드 HTML 요소의 텍스트를 가리키는 객체다.
3.노드 객체의 상속 구조
- DOM을 구성하는 노드객체는 ECMAScript 사양에 정의된 표준 빌트인 객체가 아니라 브라우저 환경에서 추가적으로 제공하는 호스트 객체이다.
- DOM은 HTML 문서의 계층적 구조와 정보를 표현하는 것은 물론 노드 객체의 종류, 즉 노드 타입에 따라 필요한 기능을 프로퍼티와 메서드의 집합인 DOM API로 제공한다 DOM API를 통해 HTML의 구조나 내용또는 스타일을 동적으로 조작할 수 있다.
- DOM이 제공하는 프로퍼티와 메서드를 사용하여 노드에 접근하고 HTML의 구조나 내용 또는 스타일 등을 동적으로 변경하는 방법을 익히는 것
- HTML을 DOM과 연관지어 바라보아야 함
39.2 요소 노드 취득
1. id를 이용한 요소 노드 취득
<body>
<ul>
<li id="apple">Apple</li>
<li id="banana">banana</li>
<li id="orange">orange</li>
</ul>
<script>
//id 값이 'banana' 인 요소 노드를 탐색하여 반환한다.
//두번째 li 요소가 파싱되어 생성된 요소 노드가 반환된다.
// getElementById 메서드는 언제나 단 하나의 요소 노드를 반환한다
const $elem = document.getElementById('banana');
//취득한 요소 노드의 style.color 프로퍼티 값을 변경한다
$elem.style.color = 'red';
</script>
</body>
</html>
2. 태그 이름을 이용한 노드 취득
//탐색된 요소 노드들은 HTMLCollection 객체에 담겨 반환된다.
//HTMLCollection 객체는 유사 배열 객체이면서 이터러블이다.
const $elem = document.getElementsByTagName('li');
//취득한 요소 노드의 style.color 프로퍼티 값을 변경한다
[...$elem].forEach(elem => {elem.style.color = 'red'})
3. class를 이용한 요소 취득
const $elem = document.getElementsByClassName('fruit');
//취득한 요소 노드의 style.color 프로퍼티 값을 변경한다
[...$elem].forEach(elem => {elem.style.color = 'red'})
const $apples = document.getElementsByClassName('fruit apple');
[...$elem].forEach(elem => {elem.style.color = 'blue'})
4. css 선택자를 이용한 요소 노드 취득
/* 전체 선택자: 모든 요소를 선택 */
* { ... }
/* 태그 선택자: 모든 p 태그 요소를 모두 선택 */
p { ... }
/* id 선택자: id 값이 'foo'인 요소를 모두 선택 */
#foo { ... }
/* class 선택자: class 값이 'foo'인 요소를 모두 선택 */
.foo { ... }
/* 어트리뷰트 선택자: input 요소 중에 type 어트리뷰트 값이 'text'인 요소를 모두 선택 */
input[type=text] { ... }
/* 후손 선택자: div 요소의 후손 요소 중 p 요소를 모두 선택 */
div p { ... }
/* 자식 선택자: div 요소의 자식 요소 중 p 요소를 모두 선택 */
div > p { ... }
/* 인접 형제 선택자: p 요소의 형제 요소 중에 p 요소 바로 뒤에 위치하는 ul 요소를 선택 */
p + ul { ... }
/* 일반 형제 선택자: p 요소의 형제 요소 중에 p 요소 뒤에 위치하는 ul 요소를 모두 선택 */
p ~ ul { ... }
/* 가상 클래스 선택자: hover 상태인 a 요소를 모두 선택 */
a:hover { ... }
/* 가상 요소 선택자: p 요소의 콘텐츠의 앞에 위치하는 공간을 선택
일반적으로 content 프로퍼티와 함께 사용된다. */
p::before { ... }
- 인수로 전달한 CSS 선택자를 만족시키는 요소 노드가 여러개인 경우 첫번째 요소 노드만 반환한다.
- 인수로 전달된 CSS 선택자를 만족시키는 요소 노드가 존재하지 않는 경우 null을 반환한다,
- 인수로 전달한 CSS 선택자가 문법에 맞지 않는 경우 DOMException 에러가 발생한다.
5. 특정 요소 노드를 취득할 수 있는 지 확인
const $elem = document.querySelector('.apple');
console.log($spple.matches('#fruits > li.apple')); //true
console.log($spple.matches('#fruits > li.banana')); //false
6. HTMLCollection과 NodeList
- 모두 유사 배열 객체면서 이터러블이다 for..of문으로 순회할 수 있으며, 스프레드 문법을 사용하여 간단히 배열로 변환할 수 있다.
- 노드의 상태 변화를 실시간으로 반영하는 살아있는 객체
- HTMLCollection
- 노드 객체의 상태 변화를 실시간으로 반영하는 살아 있는 DOM 컬렉션 객체이다.
- getElementsByTagName, getElementsByClassName
- NodeList
- HTMLCollection 객체의 부작용을 해결하기 위해 querySelectorAll 사용
- 실시간으로 노드의 객체 상태 변경을 반영하지 않는 객체
- childNodes 프로퍼티가 반환하는 NodeList 객체는 HTMLCollection 객체와 같이 실시간으로 노드의 객체의 상태 변경을 반영하는 live 객체로 동작하므로 주의가 필요
- 노드 객체의 상태 변경과 상관없이 안전하게 DOM 컬렉션을 사용하려면 HTMLCollection 이나 NodeList 객체를 배열로 반환하여 사용하는 것을 권장
39.3 노드 탐색
- 노드 탐색 프로퍼티는 모두 접근자 프로퍼티다
- setter 없이 getter만 존재하여 참조만 가능한 읽기 전용 접근자 프로퍼티다.
1. 공백 텍스트 노드
- html 요소 사이의 스페이스, 탭, 줄바꿈 등의 공백 문자는 텍스트 노드를 생성 = 공백 텍스트 노드
2. 자식 노드 탐색
3. 자식 노드 존재 확인
- Node.prototype.hasChildNodes 메서드 사용
- 존재하면 true,아니면 false 반환
4. 요소 노드의 텍스트 노드 탐색
- 요소 노드의 자식 노드
- firstChild 프로퍼티로 접근 가능
- 첫번째 자식 노드를 반환한다.
5. 부모노드 탐색
- Node.prototype.parentNode 프로퍼티를 사용한다.
- DOM 트리의 최종단 노드인 리프 노드이므로 부모 노드가 텍스트 노드인 경우는 없다.
6. 형제 노드 탐색
- 어트리뷰트 노드는 요소 노드와 연결되어 있지만 부모 노드가 같은 형제 노드가 아니기 때문에 변환되지 않는다.
- 아래 프로퍼티는 텍스트 노드 또는 요소 노드만 반환한다.
39.4 노드 정보 취득
39.5 요소 노드 텍스트 조작
1. nodeValue
- Node.prototype.nodeValue 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티
- nodeValue 프로퍼티는 참조와 할당 모두 가능함
- 노드 객체의 nodeValue 프로퍼티를 참조하려면 노드 객체의 값을 반환한다
- 노드 객체의 값이란 텍스트 노드의 텍스트다
- 텍스트 노드의 nodeValue 프로퍼티를 참조할때만 텍스트 노드의 값, 즉 텍스트를 반환한다.
- 요소 노드의 텍스트를 변경하려면 순서 처리가 필요
2. textContent
- 텍스트와 모든 자손 노드의 텍스트를 모두 취득하거나 변경
- 요소 노드의 콘텐츠 영역 내의 텍스트를 모두 반환한다.
- 모든 노드들의 텍스트 노드의 값 텍스트 모두 반환
- innerText 보다 textContext 사용 권장
39.6 DOM 조작
- DOM 조작은 새로운 노드를 생성하여 DOM에 추가하거나 기존 노드를 삭제 또는 교체 하는 것을 말한다
- DOM 조작에 의해 DOM에 새로운 노드가 추가되거나 삭제되면 리플로우와 리페인트가 발생
1. innerHTML
- setter와 getter 모두 존재하는 프로퍼티로서 요소 노드의 HTML 마크업을 취득하거나 변경한다.
- textContent 프로퍼티를 참조하면 HTML 마크업을 무시하고 텍스트만 반환하지만 innerHTML 프로퍼티는 HTML 마크업이 포함된 문자열을 그대로 반환한다.
- 사용자로부터 입력받은 데이터를 그대로 innerHTML 프로퍼티에 할당하는 것은 크로스 사이트 스크립팅 공격에 취약하므로 위험
$fruit.innerHTML = +- '<li class="banana">Banana</li>'
축약 표현
2. inserAdjacentHTML 메서드
- Element.prototype.insertAdjacentHTML(position,DOMString) 기존 요소를 제거하지 않으면서 위치를 지정해서 새로운 요소를 삽입한다.
- 두번째 인수로 전달한 HTML 마크업 문자열을 파싱하고 그결과로 생성된 노드를 첫 번째 인수로 전달한 위치에 삽입하여 DOM에 반영한다
- 첫 번째 인수로 전달할 수 있는 문자열은 beforebegin,afterbegin,beforeend,afterend의 4가지다
3. 노드 생성과 추가
- DOM은 노드를 직접 생성/삽입/삭제/치환하는 메서드도 제공한다.
요소 노드 생성
createElement
const $li = document.createElement('li');
텍스트 노드 생성
createTextNode
const textNode = document.createTextNode('banana');
텍스트 노드를 요소 노드의 자식 노드로 추가
$li.appendChild(textNode);
노드를 DOM에 추가
$fruits.appendChild($li)
4. 복수의 노드 생성과 추가
- DocumentFragement 노드는 문서, 요소,어트리뷰트,텍스트 노드와 같은 노드 객체의 일종으로, 부모 노드가 없어서 기존 DOM과는 별도로 존대한다는 특징
- 별도의 서브 DOM을 구성하여 기존 DOM 추가하기 위한 용도로 사용한다.
- 여러개의 요소 노드를 DOM에 추가하는 경우 DocumentFragement 를 사용하는 것이 더 효율적
5. 노드 삽입
- 마지막 노드로 추가
- Node.prototype.append
- 지정한 위치에 노드 삽입
- Node.prototype.insertBefore 첫번째 인수로 전달받은 노드를 두번째 인수로 전달받은 노드 앞에 삽입한다.
- 두번째 인수로 전달받은 노드는 반드시 insertBefore 메서드를 호출한 노드의 자식 노드여야 한다
6. 노드 이동
- DOM에 이미 존재하는 노드를 appendChild 또는 insertBefore 메서드를 사용하여 DOM에 다시 추가하면 현재 위치에서 노드를 제거하고 새로운 위치에 노드를 추가한다. 즉 노드 이동
7. 노드 복사
- Node.prototype.replaceChild 자신을 호출한 노드의 자식 노드를 다른 노드로 교체한다.
8. 노드 삭제
- Node.prototype.removeChild 메서드는 child 매개변수에 인수로 전달한 노드를 DOM에서 삭제한다.
39.7 어트리뷰트
1. 어트리뷰트 노드와 attributes 프로퍼티
- HTML 요소는 여러개의 어트리뷰트(속성)을 가질 수 있다.
- HTML 요소의 동작을 제어하기 위한 추가적인 정보를 제공하는 HTML 어트리뷰트는 HTML 요소의 시작 태그에 어트리뷰트 이름 = "어트리뷰트 값" 형식으로 정의
<input id = "user" type = "text" value = "ungmo2"/>
- type,value,checked 어트리뷰트는 input 요소에만 사용할 수 있다.
- 3개의 어트리뷰트 노드 생성
- 요소노드의 모든 어트리뷰트 노드는 요소 노드의 Element.prototype.attributes 프로퍼티로 취득할 수 있다.
2. HTML 어트리뷰트 조작
- Element.prototype.getAttribute/setAttribute 메서드를 사용하면 요소 노드에서 메서드를 통해 직접 HTML 어트리뷰트 값을 취득하거나 변경할 수 있어서 편리하다
- 특정 HTML 어트리뷰트가 존재하는지 확인하려면 Element.prototype.hasAttribute
- 특정 HTML 어트리뷰트가 삭제하려면 Element.prototype.removeAttribute
3. HTML 어트리뷰트 vs. DOM 프로퍼티
- 요소 노드 객체에는 HTML 어트리뷰트에 대응하는 프로퍼티가 존재한다. 이 DOM 프로퍼티들은 HTML 어트리뷰트 값을 초기값으로 가지고 있다
- DOM 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티다, 참조와 변경이 가능
- 중복 관리되고 있는 것처럼 보인다
- HTML 어트리뷰트의 역할은 HTML 요소의 초기 상태를 지정하는 것이다 즉 HTML 어트리뷰트 값은 HTML 요소의 초기 상태를 의미하며 이는 변하지 않는다.
- 요소노드는 2개의 상태 즉, 초기 상태와 최신 상태를 관리해야한다. 요소 노드의 초기 상태는 어트리뷰트 노드가 관리하며, 요소 노드의 최신 상태는 DOM 프로퍼티가 관리한다.
어트리뷰트 노드
- HTML 어트리뷰트로 지정한 HTML 요소 초기 상태는 어트리뷰트 노드에서 관리한다.
- 사용자의 입력에 의해 상태가 변경되어도 변하지 않고 html 어트리뷰트로 지정한 요소의 초기 상태를 그대로 유지한다.
document.getElementById('user').getAttribute('value');
DOM 프로퍼티
- 사용자가 입력한 최신상태 HTML 어트리뷰트에 대응하는 요소 노드의 DOM 프로퍼티가 관리한다.
- DOM 프로퍼티는 사용자의 입력에 의한 상태 변화에 반응하여 언제나 최신 상태를 유지한다.
- HTML 어트리뷰트와 DOM 프로퍼티의 대응 관계
- 대부분의 HTML 어트리뷰트는 DOM 프로퍼티와 1:1로 대응한다.
- 언제나 1:1로 대응하는 것은 아니면 HTML 어트리뷰트이름과 DOM 프로퍼티 키가 반드시 일치하는 것도 아니다.
DOM 프로퍼티 값의 타입
- getAttribute 메서드로 취득한 어트리뷰트 값은 언제나 문자열이지만 DOM 프로퍼티로 취득한 최산 상태 값은 문자열이 아닐 수도 있다.
4. data 어트리뷰트와 dataset 프로퍼티
- data 어트리뷰트와 dataset 프로퍼티를 사용하면 HTML 요소에 정의한 사용자 정의 어트리뷰트와 자바스크립트 간에 데이터를 교환할 수 있다.
- data 어트리뷰트는 data- 접두사를 붙힌다.
- dataset 프로퍼티 HTML 요소의 모든 data 어트리뷰트의 정보를 제공하는 DOMStringMap 객체를 반환.
39.8 스타일
1. 인라인 스타일 조작
- HTMLElement.protoype.style 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티로서 요소 노드의 인라인 스타일을 취득하거나 추가 또는 변경한다
$div.style.backgroundColor = 'yellow'
$div.style[background-color] = 'yellow'
2. 클래스 조작
className
- setter와 getter 모두 존재하는 접근자 프로퍼티로서 HTML 요소의 class 어트리뷰트 값을 취득하거나 변경한다.
classList
- DOMTokenList 객체 반환 class 어트리뷰트 정보를 나타내는 컬렉션 객체로서 유사 배열 객체 이터러블
add(...className)
- add 메서드는 인수로 전달한 1개 이상의 문자열을 class 어트리뷰트 값으로 추가한다
remove(...className)
item(index)
- 인수로 전달한 index에 해당하는 클래스를 class 어트리뷰트에서 반환한다.
contains(className)
- contains 메서드는 인수로 전달한 문자열과 일치하는 클래스가 class 어트리뷰트에 포함되어 있는지 확인
replace(oldClassName,newClassName)
- replace 메서드는 class 어트리뷰트에서 첫번째 인수로 전달한 문자열을 두번째 인수로 전달한 문자열로 변경
toggle(className[.force])
- class 어트리뷰트에 인수로 전달한 문자열과 일치하는 클래스가 존재하면 제거 아니면 추가
3. 요소에 적용되어 있는 css 스타일 참조
- style 프로퍼티는 인라인 스타일만 반환한다.
- 클래스를 적용한 스타일이나 상속을 통해 암묵적으로 적용된 스타일은 style 프로퍼티로 참조할 수 없다.
- HTML 요소에 적용되어 있는 모든 CSS 스타일은 참조 해야할 경우 getComputedStyle 메서드를 사용한다
39.9 DOM 표준
- HTML 과 DOM 표준은 W3C와 WHATWG 이라는 두 단체가 협력하면서 공통된 표준을 만들어 왔다.
- DOM은 현재 다음과 같이 4개의 레벨이 있다
블로그의 정보
개발 블로그👩💻
Blairj