문서 객체 모델(DOM) 이해하기

January 7, 2022

문서 객체 모델(Document object Model)은 HTML과 XML을 위한 프로그래밍 인터페이스이다.

인터페이스는 해당 타입에 어떤 속성과 메소드들이 존재해야 하는지 기술만 하고 실제 구현은 각 구현체에서 다르게 구현 할 수 있게 약속을 정의해놓는데, DOM은 인터페이스이고 크롬, 인터넷 익스플로어, 파이어폭스와 같은 브라우저에서 해당 인터페이스를 구현한다. 이렇게 인터페이스를 통해 우리는 실제 어떻게 구현됐는지 알 필요없이 원하는 기능을 사용할 수 있다.

DOM은 자바스크립트를 통해 사용할 수 있다. 그래서 우리는 자바스크립에서 DOM을 통해 문서의 구조, 스타일 그리고 내용을 변경할 수 있다. HTML문서를 브라우저가 읽으면 그 문서에 해당하는 DOM이 만들어지고, DOM은 객체 형태로 표현된다.

NOTE

HTML 문서의 태그들은 자바스크립트에서 노드가 된다. Node는 여러 하위 타입을 가지게 되고, 아래 목록으로 각 타입의 상수로 정의된다.

상수
ELEMENT_NODE 1 body, a, p, script, style, html, h1
ATTRIBUTE_NODE 2 class=”hello”
TEXT_NODE 3 HTML 문서의 텍스트들
COMMENT_NODE 8 HTML 문서의 주석들
DOCUMENT_NODE 9 document
DOCUMENT_TYPE_NODE 10 <!DOCTYPE html>

전체 노도의 종류는 문서(https://dom.spec.whatwg.org/#node)를 참고

노드의 상속 관계

<div id="div1">Hello DOM</div>

<script>
  const div1E1 = document.getElementById('div1')

  console.log(div1El.constructor)
  console.log(div1El instanceof HTMLDivElement)
  console.log(div1El instanceof HTMLElement)
  console.log(div1El instanceof Element)
  console.log(div1El instanceof Node)
  console.log(Element.prototype)
  console.log(div1El.tagName)
</script>
  • div1El의 노드 타입은 ELEMENT_NODE이고 숫자값으로 1이다. 노드는 Node 타입으로 정의되어 있고 Node 타입의 정적 속성으로 모든 노드 타입이 정의되어 있다.

  • <div> 태그는 HTMLDivElement 타입의 객체로 만들어진다. 각 태그들은 요소 노드(ELEMENT_NODE)가 되고 하위 세부 타입으로 나뉘어진다. HTMLDivElement는 HTMLElement를 상속하고 HTMLElement는 Element를 상속하고 Element는 Node를 상속한다. 그래서 div 태그는 노드이자 요소이자 HTML 요소이자 HTML Div 요소이다.

  • 각 타입은 생성자 함수로서 prototype을 통해 생성자 함수의 인스턴스들이 prototype에 정의된 속성과 메소드를 사용할 수 있다. 즉, Element.prototype에는 Element 생성자 함수를 통해 만들어진 인스턴스들이 사용할 수 있는 메소드와 속성이 정의되어 있다. 그리고 HTMLDivElement는 Element를 상속하기 때문에 HTMLDivElement의 인스턴스인 <div> 요소들은 Element의 prototype에 정의된 tagName 속성을 사용할 수 있다.

DOM 탐색하기

노드들은 트리 구조이기 때문에 부모, 자식 그리고 형제로 서로 관계를 형성한다. LI 노드들은 서로 형제 관계이고, UL은 부모 노드가 되고, text 노드들은 자식 노드가 된다.

아래는 탐색을 위한 노드의 주요 속성들이다.

노드의 관계

<li> 태그는 Node 타입이기도 하고 실제로는 HTMLLIElement 타입이기도하다. 그리고 HTMLLIElement는 HTMLElment를 상속하고 HTMLElement는 Element를 상속하고 Element는 Node를 상속한다. 그렇기 때문에 <li> 태그는 다음과 같이 Element의 속성으로도 탐색이 가능하다.

요소의 관계

여기서 중요한 점은 Node의 속성을 이용하면 텍스트노드나 코멘트 노드에 접근이 가능하고 Element의 속성은 Element만 접근이 가능하다. 그래서 적절한 속성을 이용해서 탐색해야 원하는 결과를 얻을 수 있다.

Info

Element에 접근했을때도 텍스트를 가져오는 방법은 있습니다.!!

DOM Manipulation

DOM을 조작할때 유용한 처리 방법에 대해서 설명합니다.

append vs appendChild

append는 Element의 속성이고, appendChild는 Node의 속성입니다.

부모 노드에서 자식노드를 추가할 때 사용하는 사용되는 appendChildappend는 결과에서 일부 다른 모습을 보여줍니다.
문자열을 삽입했을때 append에서는 원할하게 작동하는 반면에 appendChild에서는 Node의 값을 매개변수로 넣어주지 않으면 동작하지 않는것을 알 수 있습니다.

const body = document.body
body.append('Hello world') // success
body.appendChild('Hello world') // fail >> Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.

const div = document.createdElement('div')
div.innerText = 'Hello World'
body.append(div) // success
body.appendChild(div) // success

innerText vs textContext

textContext : Node , innerText : HTMLElement

  • innerText는 태그에 속한 글자만 가지고 온다.
  • textContext는 태그에 있는 값을 그대로 가지고온다.
<div>Hello</div>
<script>
  const div = document.querySelector('div')
  div.innerText // 'Hello'
  div.textContent // '       Hello        '
</script>

remove()

아래 코드를 보면 remove함수가 실행되는 순간 HTML 코드에서 spanBye 코드가 사라지는 것을 볼 수 있다. 하지만 여전히 spanBye 요소의 값은 그대로 살아있고 이것을 다시 append로 넣어주면 정상적으로 값을 화면 ‘Bye world~!’가 노출된다.

해당 코드를 통해서 부모에서 자식의 요소를 제거하지 않아도 자식의 요소의 값을 구할수 있다면 스스로 부모의 관계를 끊을 수 있다.

<div>
  <span id="hi">Hello world~!</span>
  <span id="bye">Bye world~!</span>
</div>
<script>
  const div = document.querySelector('div')
  const spanHi = document.querySelector('#hi')
  const spanBye = document.querySelector('#bye')

  spanBye.remove()
  console.log(spanBye)
  div.append(spanBye)
</script>

setAttribute & removeAttribute

DOM의 속성을 변경하거 제거하는 방법

classList (add, remove, toggle, contains, replace)

DOM의 class를 수정하기 방법

  • add(className) : 추가
  • remove(className) : 제거, 제거하고자 하는 클래스 이름이 없다면 제거되지 않는다. (에러 발생하지 않음)
  • toggle(className, force) : 하나의 인수만 있을 때: 클래스 값을 토글링한다. 즉, 클래스가 존재한다면 제거하고 false를 반환하며, 존재하지 않으면 클래스를 추가하고 true를 반환한다. 두번째 인수가 있을 때: 두번째 인수가 true로 평가되면 지정한 클래스 값을 추가하고 false로 평가되면 제거한다.
  • contains(string) : 지정한 클래스 값이 엘리먼트의 class 속성에 존재하는지 확인한다.
  • replace(oldClass, newClass) : 존재하는 클래스를 새로운 클래스로 교체한다.

참고

results matching ""

    No results matching ""