blair's 개발 portfolio

[모던 자바스크립트 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

활동하기