모던 자바스크립트 Deep Dive - 39. DOM

둡둡·2024년 3월 9일

Modern Javascript Deep Dive

목록 보기
40/49

39. DOM

  • DOM (Document Object Model)
    • HTML 문서의 계층적 구조와 정보를 표현함
    • 이를 제어할 수 있는 프로퍼티와 메서드(API)를 제공하는 트리 자료구조

39.1. 노드

39.1.1. HTML 요소와 노드 객체

  • HTML 요소: HTML 문서를 구성하는 개별적인 요소
    • HTML 요소 간 중첩 관계에 의한 계층적인 부자 관계 형성
  • 노드 객체: 렌더링 엔진에 의해 파싱된 HTML 요소, DOM을 구성함
    • HTML 어트리뷰트 -> 어트리뷰트 노드
    • HTML 텍스트 콘텐츠 -> 텍스트 노드
    • HTML의 부자 관계를 반영하여 모든 노드 객체들을 트리 자료구조로 구성

트리 자료구조 (tree data structure)

  • 부모 노드와 자식 노드로 구성되어, 노드 간의 계층적 구조를 표현하는 비선형 자료구조
  • 루트 노드 (root node) : 하나의 최상위 노드에서부터 시작 (부모 노드 없음, 0개 이상의 자식 노드)
  • 자식 노드가 없는 노드는 리프 노드(leaf node)
  • DOM, DOM 트리: 노드 객체들로 구성된 트리 자료구조

39.1.2. 노드 객체의 타입

  • 문서 노드 (document node)

    • DOM 트리의 최상위 루트 노드 (= document 객체)
    • 브라우저가 렌더링한 HTML 문서 전체를 가리키는 객체
    • 전역 객체 window의 document 프로퍼티에 바인딩 됨
    • 브라우저 환경의 모든 자바스크립트 코드는 하나의 전역 객체 window 공유함
    • HTML 문서당 document 객체는 유일함
    • DOM 트리의 노드들에 접근하기 위한 진입점 역할
  • 요소 노드 (element node)

    • HTML 요소를 가리키는 객체
    • HTML 요소 간 중첩에 의해 부자 관계를 가지며, 정보를 구조화
    • 문서의 구조를 표현함
  • 어트리뷰트 노드 (attribute node)

    • HTML 요소의 어트리뷰트를 가리키는 객체
    • 어트리뷰트가 지정된 HTML 요소의 요소 노드와 연결됨
    • 어트리뷰트를 참조하거나 변경하려면 요소 노드에 먼저 접근 후 어트리뷰트 노드에 접근 가능
  • 텍스트 노드 (text node)

    • HTML 요소의 텍스트를 가리키는 객체
    • 문서의 정보를 표현
    • 요소 노드의 자식 노드이며, 자식 노드를 가질 수 없는 리프 노드임
    • DOM 트리의 최종단
  • 이외에도 Comment 주석 노드, DocumentType 노드, DocumentFragment 노드 등이 있음

39.1.3. 노드 객체의 상속 구조

  • DOM을 구성하는 노드 객체는 자신의 구조와 정보를 제어할 수 있는 DOM API 사용
    • 부모, 형제, 자식을 탐색하거나 자신의 어트리뷰트와 텍스트 조작 가능
  • 노드 객체는 브라우저 환경에서 추가적으로 제공하는 호스트 객체
    • 프로토타입에 의한 상속 구조를 가짐
  • 요소 노드
    • Element 인터페이스 및 HTMElement, 태그 종류별로 세분화된 인터페이스 상속 받음
    • ex) input 요소 노드 객체 -> 프로토타입 체인에 있는 모든 프로토타입의 프로퍼티나 메서드 상속받아 사용 가능 (Object, EventTarget, Node, Element, HTMLElement, HTMLInputElement)
  • 노드 객체의 상속 구조는 개발자 도구의 Elements > Properties 확인 가능
  • 노드 객체의 종류에 따라 공통으로 갖는 기능, 또는 고유한 기능도 있음
    • HTML 요소가 객체화된 요소 노드 객체는 HTML 요소의 공통 기능을 가짐 (HTMLElement 인터페이스 - style 프로퍼티 등)
    • 공통된 기능일수록 프로토타입 체인 상위, 고유 기능일수록 하위에 상속 구조를 가짐

39.2. 요소 노드 취득

  • 요소 노드 취득은 HTML 구조나 내용 또는 스타일 등을 동적으로 조작하기 위한 시작점

39.2.1. id를 이용한 요소 노드 취득

  • Document.prototype.getElementById : 인수로 전달한 id 어트리뷰트 값을 갖는 요소 노드 탐색하여 반환
  • id는 HTML 문서 내에서 유일한 값이어야 하며, 단 하나의 요소 노드만 반환
    • 단, 중복 id 값을 갖는 여러 개가 존재하더라도 에러는 발생하지 않음
    • 첫 번째 요소 노드만 반환함
    • 존재하지 않는 경우 null 반환
  • id 어트리뷰트를 부여하면 동일한 이름의 전역 변수가 암묵적으로 선언됨
    • 동일한 이름의 전역 변수가 이미 선언된 경우 재할당 되지 않음
<div id="foo"></div>
<script>
  console.log(foo === document.getElementById('foo')); // true
</script>

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

  • Document/Element.prototype.getElementsByTagName
    • 인수로 전달한 태그 이름을 갖는 모든 요소 노드들을 탐색하여 반환
    • 여러 개의 요소 노드 객체를 갖는 DOM 컬렉션 객체 HTMLCollection 객체 반환
      • HTMLCollection: 유사 배열 객체이면서 이터러블
    • 모든 요소 노드를 취득하려면 인수로 '*' 전달
    • 존재하지 않는 경우 빈 HTMLCollection 객체 반환
  • Document 메서드: DOM 전체에서 요소 노드를 탐색하여 반환
  • Element 메서드: 특정 요소 노드의 자손 노도 중에서 탐색하여 반환

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

  • Document/Element.prototype.getElementsByClassName
    • 인수로 전달한 클래스 어트리뷰트 값을 갖는 모든 요소 노드들을 탐색하여 반환
    • 여러 개의 요소 노드 객체를 갖는 DOM 컬렉션 객체 HTMLCollection 객체 반환
    • 공백으로 구분하여 여러 개의 class 전달 가능
    • 존재하지 않는 경우 빈 HTMLCollection 객체 반환

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

  • CSS 선택자: 스타일을 적용하고자 하는 HTML 요소를 특정할 때 사용
  • Document/Element.prototype.querySelector
    • 인수로 전달한 CSS 선택자를 만족시키는 하나의 요소 노드 탐색하여 반환
    • 여러 개인 경우 첫 번째 요소 노드만 반환
    • 존재하지 않는 경우 null 반환
    • 문법에 맞지 않는 경우 DOMException 에러 발생
  • Document/Element.prototype.querySelectorAll
    • 인수로 전달한 CSS 선택자를 만족시키는 모든 요소 노드를 탐색하여 반환
    • 여러 개의 요소 노드 객체를 갖는 DOM 컬렉션 객체인 NodeList 객체 반환
      • 유사 배열 객체이면서 이터러블
    • 존재하지 않는 경우 빈 NodeList 객체 반환
    • 문법에 맞지 않는 경우 DOMException 에러 발생
    • 모든 요소 노드를 취득하려면 인수로 '*' 전달
  • CSS 선택자 문법의 경우 비교적 성능 느림
    • id 값이 있는 경우 getElementById 사용
    • 이외에는 querySelector, querySelectorAll 사용 권장

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

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

39.2.6. HTMLCollection과 NodeList

  • HTMLCollectionNodeList
    • 유사 배열 객체이면서 이터러블 -> for ... of문 순회 가능
    • 스프레드 문법이나 Array.from 메서드를 통해 배열로 변환하여 사용 권장

HTMLCollection

  • 노드 객체의 상태 변화를 실시간으로 반영하는 live DOM 컬렉션 객체
  • 실시간 상태 변경을 반영하여 요소가 제거될 수 있기 때문에 for문 사용 지양
    • for 문을 역방향으로 순회하거나 while문으로 대체 가능
    • 배열 고차 함수 사용 권장 (forEach, map, filter, reduce 등)

NodeList

  • 대부분 노드 객체의 상태 변화를 반영하지 않는 non-live 객체
    • childNodes 프로퍼티가 반환하는 NodeList 객체는 live 객체로 동작함

39.3. 노드 탐색

  • DOM 트리 노드 탐색 프로퍼티
    • getter만 존재하는 읽기 전용 접근자 프로퍼티

39.3.1. 공백 텍스트 노드

  • 공백 텍스트 노드: HTML 요소 사이의 스페잇, 탭, 줄바꿈(개행) 등의 공백 문자(white space)

39.3.2. 자식 노드 탐색

  • Node.prototype.childNodes
    • 모든 자식 노드를 NodeList 반환
    • 텍스트 노드 포함
  • Node.prototype.firstChild
    • 첫 번째 자식 노드 반환
    • 텍스트 노드이거나 요소 노드
  • Node.prototype.lastChild
    • 마지막 자식 노드 반환
    • 텍스트 노드이거나 요소 노드
  • Element.prototype.children
    • 자식 노드의 모든 요소 노드만 HTMLCollection 반환
    • 텍스트 노드 포함하지 않음
  • Element.prototype.firstElementChild
    • 첫 번째 자식 노드의 요소 노드만 반환
  • Element.prototype.lastElementChild
    • 마지막 자식 노드의 요소 노드만 반환

39.3.3. 자식 노드 존재 확인

  • Node.prototype.hasChildNodes
    • 인수로 전달한 자식 노드의 존재 여부를 boolean 반환
    • 텍스트 노드를 포함하여 존재 확인함

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

  • 요소 노드의 텍스트 노드는 해당 요소의 자식 노드
    • firstChild 프로퍼티로 접근 가능

39.3.5. 부모 노드 탐색

  • Node.prototype.parentNode
    • 부모 노드가 텍스트 노드인 경우는 없음

39.3.6. 형제 노드 탐색

  • Node.prototype.previousSibling
    • 자신의 이전 형제 노드 반환
    • 텍스트 노드 포함
  • Node.prototype.nextSibling
    • 자신의 다음 형제 노드 반환
    • 텍스트 노드 포함
  • Element.prototype.previousElementSibling
    • 자신의 이전 형제 노드 반환
    • 텍스트 노드를 제외한 요소 노드만 반환
  • Element.prototype.nextElementSibling
    • 자신의 다음 형제 노드 반환
    • 텍스트 노드를 제외한 요소 노드만 반환

39.4. 노드 정보 취득

  • Node.prototype.nodeType
    • 노드 객체의 종류, 타입을 나타내는 상수 반환
    • Node.ELEMENT_NODE : 요소 노드 타입인 상수 1 반환
    • Node.TEXT_NODE : 텍스트 노드 타입인 상수 3 반환
    • NOde.DOCUMENT_NODE : 문서 노드 타입인 상수 9 반환
  • Node.prototype.nodeName
    • 노드 이름 문자열 반환
    • 요소 노드 : 태그 이름 대문자 문자열 반환
    • 텍스트 노드 : 문자열 '#text' 반환
    • 문서 노드 : 문자열 '#document' 반환

39.5. 요소 노드의 텍스트 조작

39.5.1. nodeValue

  • Node.prototype.nodeValue
    • getter/setter로 참조와 할당이 모두 가능한 접근자 프로퍼티
    • 참조 시 텍스트 노드의 텍스트 반환
      • 문서 노드나 요소 노드 참조 시 null 반환
    • 할당 시 텍스트 노드의 값 변경 가능

39.5.1. textContent

  • Node.prototype.textContent
    • getter/setter로 참조와 할당이 모두 가능한 접근자 프로퍼티
    • 모든 자손 노드의 텍스트 조작 가능
    • 참조 시 콘텐츠 영역 내의 모든 텍스트 반환, HTML 마크업 무시됨
    • 콘텐츠 영역에 자식 요소 노드가 없고 텍스트만 존재하는 경우 간편함
    • 할당 시 모든 자식 노드가 제거되고 할당한 문자열이 텍스트로 추가됨
      • HTML 마크업 파싱되지 않고 문자열 그대로 추가됨
      • innerHTML 유사함, 권장하지 않음 (CSS 종속적, 느림)

39.6. DOM 조작

39.6.1. innerHTML

  • Element.prototype.innerHTML
    • getter/setter 모두 존재하는 접근자 프로퍼티
    • 콘텐츠 영역 내 모든 요소 노드의 HTML 마크업 포함하여 문자열로 반환
    • 할당 시 모든 자식 노드 제거되고 HTML 파싱되어 DOM에 반영
  • 크로스 사이트 스크립팅 공격 취약
    • HTML5는 innerHTML 프로퍼티로 삽입된 자바스크립트 코드 실행하지 않음
  • 모든 자식 노드를 제거하고 재할당하여 비효율적이고 느림

39.6.2. insertAdjacentHTML

  • Element.prototype.insertAdjacentHTML(position, DOMString)
    • 기존 요소를 제거하지 않고 지정한 위치에 새로운 요소 삽입
    • 첫 번째 인수로 전달한 위치에 두 번째 인수로 전달한 문자열을 HTML 마크업 파싱하여 DOM에 반영
    • 첫 번째 인수: 위치 지정 4가지
      • beforebegin
      • afterbegin
      • beforeend
      • afterend
  • 크로스 사이트 스크립팅 공격 취약

39.6.3. 노드 생성과 추가

요소 노드 생성

  • Document.prototype.createElement(tagName)
    • 인수로 전달 받은 태그 요소 노드를 생성하여 반환
    • 기존 DOM에 추가되지 않고 홀로 존재하는 상태
      • 자식 노드나 텍스트 노드가 없음

텍스트 노드 생성

  • Document.prototype.createTextNode(text)
    • 인수로 전달 받은 텍스트로 텍스트 노드 생성하여 반환
    • 부모 노드 없이 홀로 존재하는 상태

요소 노드의 자식 노드 및 DOM 추가

  • Node.prototype.appendChild(childNode)
    • 인수로 전달한 노드를 호출한 노드의 마지막 자식 노드로 추가
    • (BUT. 추가하려는 요소 노드에 자식 노드가 하나도 없을 때 텍스트 노드만 추가하려는 경우 textContent가 간편함)
  • 기존 DOM에 새로운 요소 추가 시 DOM이 한 번 변경됨
    • 리플로우와 리페인트 실행

39.6.4. 복수의 노드 생성과 추가

  • 여러 개의 노드를 추가하는 경우 DOM이 여러 번 변경되는 문제 발생
    • 컨테이너 요소(div)를 생성하여 자식 노드로 추가한 후 컨테이너를 DOM에 추가
    • 단, 불필요한 컨테이너 요소(div)가 추가되는 부작용
  • DocumentFragment
    • Document.prototype.createDocumentFragment
    • 노드 객체의 일종
    • 부모 노드 없이 기존 DOM과 별도로 존재함
    • 위 컨테이너 요소와 같이 별도의 서브 DOM을 구성하기 위한 용도
    • DOM 추가시 자신은 제거되고 자신의 자식 노드만 추가됨

39.6.5. 노드 삽입

마지막 노드로 추가

  • Node.prototype.appendChild
    • 삽입 위치 지정 불가, 마지막 자식 노드로 추가

지정한 위치에 노드 삽입

  • Node.prototype.insertBefore(newNode, childNode)
    • 첫 번째 요소 노드를 두 번째 노드 앞에 삽입
    • 두 번째 노드가 호출한 노드의 자식 노드가 아니면 DOMException 발생
    • 두 번째 노드가 null인 경우 마지막 자식 노드로 추가됨 (=appendChild)

39.6.6. 노드 이동

  • DOM에 새로운 요소들을 추가하면 기존 노드들이 이동함

39.6.7. 노드 복사

  • Node.prototype.cloneNode([deep: true | false])
    • 노드를 복사하여 반환함
    • (기본 값, false) 얕은 복사: 노드 자신만 사본 생성 (텍스트 노드도 없음)
    • (true) 깊은 복사: 모든 자손 노드를 포함한 사본 생성

39.6.8. 노드 교체

  • Node.prototype.replaceChild(newChild, oldChild)
    • 호출한 노드의 자식 노드를 다른 노드로 교체함
    • oldChild 노드는 DOMd에서 제거됨
    • oldChild가 호출한 노드의 자식 노드가 아니면 DOMException 발생

39.6.9. 노드 삭제

  • Node.prototype.removeChild(child)
    • 인수로 전달한 노드를 DOM에서 삭제함
    • 전달한 노드는 호출한 노드의 자식 노드여야 함

39.7. 어트리뷰트

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

  • HTML 요소는 여러 개의 어트리뷰트(속성) 가질 수 있음
  • 어트리뷰트는 HTML 요소의 시작 태그에 "이름='값'" 형식으로 정의
    • ex. <input id="user" type="text" value="ungmo2">
  • 모든 HTML 요소에서 사용 가능한 공통 어트리뷰트
    • 글로벌 어트리뷰트(id, class, style 등)와 이벤트 핸들러 어트리뷰트 등
  • 특정 HTML 요소에서 사용 가능한 어트리뷰트
    • input : type, value, checked 등
  • HTML 어트리뷰트당 하나의 어트리뷰트 노드 생성
    • 3개의 어트리뷰트가 있으면 3개의 어트리뷰트 노드 생성
    • 모든 어트리뷰트 노드의 참조는 유사 배열 객체이자 이터러블인 NamedNodeMap 객체에 담긴 attributes 프로퍼티에 저장
  • Element.prototype.attributes
    • getter만 존재하는 읽기 전용 접근자 프로퍼티
    • 요소 노드의 모든 어트리뷰트 노드의 참조가 담긴 NamedNodeMap 객체 반환

39.7.2. HTML 어트리뷰트 조작

  • Element.prototype.getAttribute/setAttribute
    • HTML 어트리뷰트 값을 취득하거나 변경 가능
    • getAttribute(attributeName)
    • setAttribute(attributeName, attributeValue)
  • Element.prototype.hasAttribute(attributeName)
    • 특정 HTML 어트리뷰트 존재 여부 확인
  • Element.prototype.removeAttribute(attributeName)
    • 특정 HTML 어트리뷰트 삭제

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

  • 요소 노드 객체는 HTML 어트리뷰트에 대응하는 DOM 프로퍼티를 가짐
  • DOM 프로퍼티
    • getter/setter 모두 존재하는 접근자 프로퍼티
  • HTML 어트리뷰트
    • HTML 요소의 초기 상태 지정
    • 첫 렌더링까지 어트리뷰트 값 = 요소 노드 value = HTML 어트리뷰트 값
    • 상태(state)를 가진 요소 노드가 변경되면 DOM 프로퍼티 변경됨
  • 요소 노드는 초기 상태와 최신 상태 2가지 관리
    • 초기 상태 = 어트리뷰트 노드
    • 최신 상태 = DOM 프로퍼티

어트리뷰트 노드

  • HTML 요소의 초기 상태 값 관리
  • getAttribute / setAttribute

DOM 프로퍼티

  • 사용자의 입력에 의한 상태 변화에 반응하여 동적으로 변경되는 최신 상태 값 관리
  • 모든 DOM 프로퍼티가 아닌 관계 있는 DOM 프로퍼티가 관리
    • ex. input 요소에서 사용자 입력 상태 변화는 value, checked 프로퍼티가 관리

HTML 어트리뷰트와 DOM 프로퍼티의 대응 관계

  • 대부분 HTML 어트리뷰트와 DOM 프로퍼티 동일한 이름으로 1:1 대응
    • id 어트리뷰트 : id 프로퍼티
    • value 어트리뷰트 (초기 상태) : value 프로퍼티 (최신 상태)
    • class 어트리뷰트 : className, classList 프로퍼티
    • for 어트리뷰트 : htmlFor 프로퍼티
    • td > colspan 대응하는 프로퍼티 없음
    • textContent 프로퍼티 대응하는 어트리뷰트 없음
    • 어트리뷰트는 대소문자 구별하지 않음, 프로퍼티 키는 카멜 케이스

DOM 프로퍼티 값의 타입

  • getAttribute 로 취득한 어트리뷰트 값은 항상 문자열
  • DOM 프로퍼티는 항상 문자열은 아님
    • ex. checked 프로퍼티 값은 불리언 타입

39.7.4. data 어트리뷰트와 dataset 프로퍼티

  • 사용자 정의 어트리뷰트와 자바스크립트 간 데이터 교환
  • data- 접두사 다음 임의 이름 붙임
  • HTMLElement.dataset 프로퍼티로 취득
    • DOMStringMap 객체 반환
    • 접두사 다음에 붙인 이름을 카멜 케이스로 변환한 프로퍼티 가짐
    • 존재하지 않는 이름으로 할당하면 data 어트리뷰트 추가됨

39.8. 스타일

39.8.1. 인라인 스타일 조작

  • HTMLElement.prototype.style
    • getter/setter 모두 존재하는 접근자 프로퍼티
    • 요소 노드의 인라인 스타일을 취득하거나 변경함
    • CSSStyleDeclaration 타입 객체 반환
    • CSS 프로퍼티 케밥 케이스 -> CSSStyleDeclaration 카멜 케이스

39.8.2. 클래스 조작

className

  • Element.prototype.className
    • getter/setter 모두 존재하는 접근자 프로퍼티
    • HTML 요소의 class 어트리뷰트 값을 취득하거나 변경함
    • 문자열로 반환하거나 할당

classList

  • Element.prototype.classList
    • class 어트리뷰트의 정보를 담은 DOMTokenList 객체 반환
  • DOMTokenList
    • 유사 배열 객체이면서 이터러블
    • add(...className) : 전달한 문자열을 class 어트리뷰트 값으로 추가
    • remove(...className) : 전달한 문자열과 일치하는 클래스 삭제, 없으면 무시
    • item(index) : 전달한 index에 해당하는 클래스 반환
    • contains(className) : 전달한 문자열과 일치하는 클래스 포함 여부 확인
    • replace(oldClassName, newClassName) : 첫 번째 인수 문자열을 두 번째 인수 문자열로 변경
    • toggle(className[.force]) : 인수로 전달한 클래스가 존재하면 제거, 존재하지 않으면 추가
      • 두 번째 인수 true/false 강제 추가 및 제거
    • 이외에 forEach, entries, keys, values 등 메서드 제공

39.8.3. 요소에 적용되어 있는 CSS 스타일 참조

  • getComputedStyle(element[, pseudo])
    • 클래스나 상속을 포함한 HTML 요소에 적용된 모든 평가된 스타일 참조
    • 평가된 스타일: 링크, 임베딩, 인라인, 자바스크립트, 상속 등 모든 스타일이 조합되어 최종적으로 적용된 스타일
    • 첫 번째 인수로 전달한 요소 노드에 평가된 스타일이 담긴 CSSStyleDelaration 객체 반환
    • 두 번째 인수로 의사(가상) 요소 지정 문자열 전달 (:after, :before)

39.9. DOM 표준

  • DOM 4개의 레벨(버전)
    • DOM Level 1
    • DOM Level 2
    • DOM Level 3
    • DOM Level 4

[출처] 모던 자바스크립트, Deep Dive

profile
괴발개발라이프

0개의 댓글