[ 모던 자바스크립트 Deep Dive ] 39장 : DOM

박새롬·2024년 4월 16일
0
post-thumbnail
  • DOM은 HTML 문서의 계층적 구조와 정보를 표현하며 이를 제어할 수 있는 API
    • 즉 프로퍼티와 메서드를 제공하는 트리 자료구조다.

39.1 노드

39.1.1 HTML 요소와 노드 객체

  • HTML 요소는 HTML 문서를 구성하는 개별적인 요소를 의미한다.
  • HTML 요소는 렌더링 엔진에 의해 파싱되어 DOM을 구성하는 요소 노드 객체로 변환
  • HTML 요소 간에는 중첩 관계를 가질 수 있으며 이러한 관계를 반영하여 HTML 요소를 객체화한 모든 노드 객체들을 트리 자료 구조로 구성

39.1.2 노드 객체의 타입

  • 다음 HTML 문서를 렌더링 엔진인 파싱한다고 생각해보자

  • 렌더링 엔진은 위 HTML 문서를 파싱하여 다음과 같이 DOM을 생성한다.

  • 노드 객체는 상속 구조를 가지고 총 12개의 종류가 있다.

  • 중요한 타입은 다음과 같이 4가지다.

  • 1.문서 노드(document node)

    • DOM 트리의 최상위에 존재하는 루트 노드, document 객체를 가리킴
    • document 객체는 브라우저가 렌더링한 HTML문서 전체를 가리키는 객체
    • window.document 혹은 document로 참조 가능
    • script 태그로 자바스크립트 파일이 분리되어 있어도 전역 객체 window.document에 바인딩된 객체를 따름 (HTML 문서당 document객체는 유일)
    • document객체는 DOM트리의 루트 노드 이므로 DOM 트리의 노드들에 접근하기 위한 진입점 역할
  • 2.요소 노드(element node)

    • HTML 요소를 가리키는 객체
    • 각 HTML 요소들은 상속관계를 가지므로, 요소 노드는 문소의 구조를 표현
  • 3.어트리뷰트 노드(attribute node)

    • HTML 요소의 어트리뷰트를 가리키는 객체
    • 지정된 HTML 요소의 노드와 연결되어 있다.
    • 요소 노드에만 연결되어 있고 부모 노드를 가지지 않음
    • 따라서, 어트리뷰트 노드에 접근하려면 요소 노드에 먼저 접근 해야함
  • 4.텍스트 노드(text node)

    • HTML 요소의 텍스트를 가리키는 객체
    • 요소 노드의 자식 노드
    • 자식 노드를 가질 수 없는 리프 노드(leaf node) 즉, DOM 트리의 최종단
    • 따라서 , 텍스트 노드에 접근하려면 부모노드인 요소 노드에 접근 해야함

39.1.3 노드 객체의 상속구조

  • DOM 을 구성하는 노드 객체는 표준 빌트인 객체가 아니라 브라우저 환경에서 추가적으로 제공하는 호스트 객체
  • 노드 객체도 자바스크립트 객체이므로 프로토타입에 의한 상속 구조를 가진다.
  • 모든 노드 객체는 Object, EventTarget, Node 인터페이스를 상속한다.
  • HTML 요소의 종류에 따라 고유의 기능을 가지기도 한다.

    DOMHTML 문서의 계층적 구조와 정보를 표현하는 것은 물론 노드 객체의 종류이다.

→ 노드 타입에 따라 필요한 기능을 프로퍼티와 메서드의 집합인 **DOM API로 제공한다.**

→ 이 DOM API를 통해 HTML의 구조나 내용 또는 스타일 등을 동적으로 조작 가능하다.


39.2 요소 노드 취득

39.2.1 id를 이한 요소 노드 취득

  • Document.prototype.getElementById 메서드 사용한다.
  • 인수로 전달된 id 값을 갖는 첫 번째 요소 노드만 반환한다.

39.2.2 태그 이름을 이용한 요소 노드 취득

  • Document.prototype/Element.prototype.getElementByTagName 메서드는 인수로 전달한 태그 이름을 갖는 모든 요소 노드들을 탐색하여 반환한다.
  • 함수는 하나의 값만 반환할 수 있으므로 여러 개의 값을 반환하려면 배열이나 객체와 같은 자료구조에 담아 반환해야 한다.

39.2.3 class를 이용한 요소 노드 취득

  • Document.prototype/Element.prototype.getElementByClassName 메서드는 인수로 전달한 class 어트리뷰트 값이 갖는 모든 요소 노드들을 탐색하여 반환한다.

39.2.4 CSS 선택자를 이용한 요소 노드 취득

  • CSS 선택자는 스타일을 적용하고자 하는 HTML 요소를 특정할 때 사용하는 문법이다.
  • Document.prototype/Element.prototype.querySelecter메서드는 인수로 전달한 CSS선택자를 만족시키기는 하나의 요소 노드를 탐색하여 반환한다.
    • 인수로 전달한 CSS 선택자를 만족시키는 요소 노드가 여러 개인 경우 첫 번째 요소 노드만 반환한다.
    • 인수로 전달한 CSS 선택자를 만족시키는 요소 노드가 존재하지 않는 경우 null을 반환한다
    • 인수로 전달한 CSS 선택자가 문법에 맞지 않는 경우 DOMException 에러가 발생한다.

39.2.5 특정 요소 노드를 취득할 수 있는지 확인

  • Element.prototype.matches메서드는 인수로 전달한 CSS 선택자를 통해 특정 요소 노드를 취득할 수 있는지 확인한다.
    • Element.prototype.matches메서드는 이벤트 위임을 사용할 때 유용하다.

39.2.6 HTMLCollection과 NodeList

HTMLCollection

  • HTMLCollection은 객체의 상태 변화를 실시간으로 반영한다.
  • 살아 있는 객체라고 부르기도 한다.
  • 상태가 즉시 반영 되기 때문에 에러가 나기도 함, 이를 해결하기 위해 배열로 변환 하여 사용 권장한다.
// class 값이 'red'인 요소 객체를 모두 HTMLCollection 으로 반환
const $elems = document.getElementsByClassName('red');// 객체 상태가 실시간으로 반영되면서 for문 으로 사용할 경우 원하는 대로 조작하기 어려움
// 배열로 변환해서 사용

[...$elems].forEach(ele => ele.className = 'blue');

NodeList

  • 실시간으로 객체의 상태 변경을 반영하지 않는 객체
  • NodeList.prototype.forEach 메서드를 상속 받아 사용할 수 있다.
  • 예외로, childNodes 프로퍼티가 반환하는 NodeList 객체는 HTMLCollection과 같이 상태가 실시간으로 반영한다

노드 객체의 상태 변경과 상관없이 사용하려면 HTMLColleciton 이나 NodeList 객체를 배열로 변환해서 사용을 권장한다.

두 객체는 모두 유사 배열이면서 이터러블이다.

→ 스프레드 문법으로 배열로 변환 가능하다.


39.3 노드탐색

  • DOM 트리 상의 노드를 탐색할 수 있도록 Node,Element 인터페이스는 트리 탐색 프로퍼티를 제공한다.
    • 노드 탐색 프로퍼티는 모두 접근자 프로퍼티다.

39.3.1 공백 텍스트 노드

  • HTML 요소 사이의 스페이스, 탭, 줄바꿈 등의 공백 문자는 텍스트 노드를 생성한다.

39.3.2 자식 노드 탐색

  • 자식 노드를 탐색하기 위해서는 다음과 같은 노트 탐색 프로퍼티를 사용한다.

39.3.3 자식 노드 존재 확인

  • 자식 노드가 존재하는지 확인하려면 Node.prototype.hasChildNodes메서드를 사용한다.
    • 자식 노드가 존재하면 true, 존재하지않으면 false를 반환한다.

39.3.4 요소 노드의 텍스트 노드 탐색

  • 요소 노드의 텍스트 노드는 요소 노드의 자식노드다
  • 따라서 요소 노드의 텍스트 노드는 firstChild 프로퍼티로 접근할 수 있다.

39.3.5 부모노드 탐색

  • 부모 노드를 탐색하려면 Node.prototype.parentNode프로퍼티를 사용한다.

39.3.6 형재 노드 탐색

  • 부모 노드가 강ㅌ은 형제 노드를 탐색하려면 다음과 같은 노드 탐색 프로퍼티를 사용한다.

39.4 노드 정보 취득

  • 노드 객체에 대한 정보를 취득하려면 다음과 같은 노드 정보 프로퍼티를 사용한다.

39.5 요소 노드의 텍스트 조작

39.5.1 NodeValue

  • 노드 객체의 값(텍스트 노드의 텍스트)을 반환한다.
  • 문서 노드나 요소 노드는 nodeValue 참조하면 null 반환한다.
<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello</div>
  </body>
  <script>
 <-- 1. #foo 요소 노드의 자식 노드인 텍스트 노드를 취득한다. -->
    const $textNode = document.getElementById('foo').firstChild;
 
<-- 2. nodeValue 프로퍼티를 사용하여 텍스트 노드의 값을 변경한다. -->
    $textNode.nodeValue = 'World'; 
  </script>
</html>

39.5.2 textContent

  • 요소 노드의 텍스트와 자손 노드의 텍스트를 모두 취득한다.
    • 즉, textContent는 요소노드의 콘텐츠 영역(시작 태그와 종료태그 사이)의 모든 텍스트를 모두 반환한다.
    • 이때 HTML 마크업은 무시됨
<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello <span>world!</span></div>
  </body>
  <script>
    // #foo 요소 노드의 텍스트를 모두 취득한다. 이때 HTML 마크업은 무시된다.
    console.log(document.getElementById('foo').textContent); // Hello world!
  </script>
</html>

39.6 DOM 조작

39.6.1 innerHTMl

  • 요소 노드의 콘텐츠 영역 사이에 포함된 모든 HTML 마크업을 문자열로 반환
  • innerHTML 프로퍼티에 문자열을 할당하면 요소노드의 모든 자식노드가 제거되고 할당한 문자열의 파싱되어 DOM에 반영
<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li class="apple">Apple</li>
    </ul>
  </body>
  <script>
    const $fruits = document.getElementById('fruits');
    <-- 노드 추가 --> 
    $fruits.innerHTML += '<li class="banana">Banana</li>';
 
    <-- 노드 교체 --> 
    $fruits.innerHTML = '<li class="orange">Orange</li>';
 
    <-- 노드 삭제 --> 
    $fruits.innerHTML = '';
  </script>
</html>
  • innerHTML 단점
    • 크로스 사이트 스크립트 공격에 취약하다.
    • 모든 자식요소를 제거하고 할당하기 때문에 비효율적이다.
    • 새로운 요소를 삽입할 때 위치를 지정할 수 없다.

39.6.2 insertAdjacentHTML

  • 기존 요소를 제거하지 않으면서 위치를 지정해 새로운 요소를 삽입한다
  • 첫 번재 인수로 위치를 지정한다 (beforebegin, afterbegin, beforend, afterend)
<!DOCTYPE html>
<html>
  <body>
    <!-- beforebegin -->
    <div id="foo">
      <!-- afterbegin -->
      text
      <!-- beforeend -->
    </div>
    <!-- afterend -->
  </body>
</html>

요소 노드 생성

  • Document.prototype.createElement(tagName) 메서드는 요소 노드를 생성하여 반환한다.
  • tagName 인수에는 태그 이름을 나타내는 문자열을 전달한다.
  • createElement 메서드로 생성한 요소 노드는 기존 DOM에 추가되지 않고 홀로 존재하는 상태로, 이후에 DOM에 추가하는 처리가 필요하다.
  • createElement 메서드로 생성한 요소 노드는 아무런 자식 노드가 없는 상태이다.

텍스트 노드 생성

  • Document.prototype.createTextNode(text) 메서드는 텍스트 노드를 생성하여 반환한다.
  • createTextNode 메서드로 생성한 텍스트 노드는 요소 노드의 자식 노드에 추가되지 않고 홀로 존재하는 상태로, 이후에 요소 노드에 추가하는 처리가 필요하다.

텍스트 노드를 요소 노드의 자식 노드로 추가

  • Node.prototype.appendChild(childNode) 메서드는 인수로 전달한 노드를 메서드를 호출한 노드의 마지막 자식 노드로 추가한다.
  • childNode 인수에는 추가할 자식 노드를 전달한다.
  • 요소 노드에 자식 노드가 하나도 없는 경우에는 textContent 프로퍼티를 사용하여 텍스트 노드를 추가하는 것이 더욱 간편하다.
  • 요소 노드에 자식 노드가 있는 경우 textContent 프로퍼티에 문자열을 할당하면 요소 노드의 모든 자식 노드가 제거되고 할당한 문자열이 텍스트로 추가되므로 주의!

39.7 어트리뷰트

39.7.1 어트리뷰트 노드와 attrubutes 프로퍼티

  • HTML 문서가 파싱 될 때 어티리뷰트는 어티리뷰트 노드로 변환 되어 요소 노드와 연결 된다.
  • 하나의 어트리뷰트당 하나의 어트리뷰트 노드 생성한다.
  • 이때 모든 어트리뷰트 노드의 참조는 유사 배열 객체이자 이터러블인 NamedNodeMap 객체에 담겨서 요소 노드의 attributes 프로퍼티에 저장한다.

39.7.2 HTML 어트리뷰트 조작

  • Element.prototype.getAttirbute(attributeName) : 어트리뷰트 값을 참조
  • Element.prototype.setAttirbute(attributeName, attributeValue) : 어트리뷰트 값을 변경
  • Element.prototype.hasAttirbute(attributeName) : 어트리뷰트 존재하는지 확인
  • Element.prototype.removeAttirbute(attributeName) : 특정 어트리뷰트 삭제

39.7.3 HTML 어트리뷰트 vs DOM 프로퍼티

  • 요소 노드 객체에는 HTML 어트리뷰트에 대응 하는 DOM 프로퍼티가 존재한다.
  • 즉, HTML 어트리 뷰트는 중복 관리 되는 것처럼 보인다.
    • 요소 노드의 attributes 프로퍼티에서 관리하는 어트리뷰트 노드
    • 요소 노드의 DOM 프로퍼티

39.8 스타일

39.8.1 인라인 스타일 조작

  • HTMLElement.prototype.style 프로퍼티
  • getter / setter 가 모두 존재하는 접근자 프로퍼티로 요소 노드의 인라인 스타일을 취득하거나 추가 또는 변경한다.
<!DOCTYPE html>
<html>
<body>
  <div style="color: red">Hello World</div>
  <script>
    const $div = document.querySelector('div');
 
    <-- 인라인 스타일 취득 -->
    console.log($div.style); // CSSStyleDeclaration { 0: "color", ... }
 
    <-- 인라인 스타일 변경 -->
    $div.style.color = 'blue';
 
    <-- 인라인 스타일 추가 -->
    $div.style.width = '100px';
    $div.style.height = '100px';
    $div.style.backgroundColor = 'yellow';
  </script>
</body>
</html>

39.8.2 클래스 조작

  • class 어트리뷰트에 대응하는 DOM 프로퍼티는 class 가 아니라 classNameclassList

className

  • Element.prototype.className
  • class 어트리뷰트의 값을 문자열로 반환한다.
  • 클래스 값이 공백으로 구분되어 있을 경우 그대로 문자열로 반환한다.

classList

  • Element.prototype.classList
  • class 어트리뷰트의 정보를 담은 DOMTokenList 객체를 반환한다.
  • DOMTokenList 객체는 class 어트리뷰트의 정보를나타내는 컬렉션 객체로 유사 배열이면서 이터러블
    • DOMTokenList 주요 메서드
    • add(... className) : 인수로 전달된 1개의 이상의 문자열을 class 어트리뷰트 값으로 추가
    • remove(... className) : 인수로 전달한 1개 이상의 문자열과 일치하는 class 어트리뷰트에서 삭제한다.(일치 하는 클래스가 없으면 무시됨)
    • item(index) : 인수로 전달한 index에 해당하는 클래스를 class 어트리뷰트에서 반환한다.
    • contains(className) : 인수로 전달한 문자열과 일치하는 클래스가 class 어트리뷰트에 포함되어 있는지 확인한다.
    • replace(oldClassName, newClassName) : 첫 번째 인수 클래스를 두번째 클래스로 변경한다.
    • toggle(className[, force]) : 인수로 전달한 문자열과 일치하는 클래스가 존재하면 제거하고 존재하지 않으면 추가한다.
profile
열심히 하고싶은 사람

0개의 댓글