개발자 Saaad

[JavaScript] DOM 탐색하기 본문

학습/kakao X goorm 풀스택12회차

[JavaScript] DOM 탐색하기

Saaad 2024. 11. 27. 14:50

시작하기 앞서

https://ko.javascript.info/dom-navigation

 

DOM 탐색하기

 

ko.javascript.info

위 글을 참고하여 작성했음을 알립니다.


DOM 탐색

DOM을 이용하면 요소와 요소의 콘텐츠에 무엇이든 할 수 있습니다. 하지만 무언가를 하기 전엔, 당연히 조작하고자 하는 DOM 객체에

접근하는 것이 선행되어야 합니다.

DOM에서 수행하는 모든 연산은 document 객체에서 시작합니다. document 객체는 DOM에 접근하기 위한 '진입점' 입니다.

 

아래 그림은 DOM 노드 탐색이 어떤 관계를 통해 이루어지는지 보여줍니다.

 

위 그림에 대해서 자세히 설명해보겠습니다.

 

 


트리 상단의 documentElement와 body

DOM 트리 상단의 노드들은 document 가 제공하는 프로퍼티를 사용해 접근할 수 있습니다.

 

 " <html> = document.documentElement "

즉 html 태그를 가리키고 싶다면 위와 같이 접근할 수 있겠습니다.

 

document를 제외하고 DOM 트리 꼭대기에 있는 문서 노드는 <html> 태그에 해당하는 

document.documentElement 입니다.

 

" <body> = document.body "

document.body 는 <body> 요소에 해당하는 DOM 노드로, 자주 쓰이는 노드 중 하나입니다.

 

" <head> = document.head "

<head> 태그는 document.head 로 접근할 수 있습니다.

 

 

! document.body 가 null 일수도 있으니 주의하세요. ! 

더보기

스크립트를 읽는 도중에 존재하지 않는 요소는 스크립트에서 접근할 수 없습니다.

브라우저가 아직 document.body를 읽지 않았기 때문에 <head> 안에 있는 스크립트에서는 document.body에 접근할 수 없습니다.

 

따라서 아래 예시에선 첫 번째 alert 창엔 null이 출력됩니다.

<!doctype html>
<html>

<head>
  <script>
    alert( "HEAD: " + document.body ); // null, 아직 <body>에 해당하는 노드가 생성되지 않았음
  </script>
</head>

<body>

  <script>
    alert( "BODY: " + document.body ); // HTMLBodyElement, 지금은 노드가 존재하므로 읽을 수 있음
  </script>

</body>
</html>

 

! DOM의 세계에선 null 은 '존재하지 않음' 을 의미합니다. ! 

 


childNodes, firstChild, lastChild 로 자식 노드 탐색

- 자식 노드(child node, children) 는 바로 아래 자식 요소를 나타냅니다.

자식 노드는 부모 노드의 바로 아래에서 중첩관계를 만듭니다.

 

- 후손 노드(descendants) 는 중첩 관계에 있는 모든 요소를 의미합니다.

자식 노드, 자식 노드의 모든 자식 노드 등이 후손 노드가 됩니다.

 

아래 예시에서 <body> 는 <div>와 <ul>, 몇 개의 빈 텍스트 노드를 자식 노드로 갖습니다.

<html>
<body>
  <div>시작</div>

  <ul>
    <li>
      <b>항목</b>
    </li>
  </ul>
</body>
</html>

 

<div>나 <ul> 같은 <body> 의 자식 요소 뿐만 아니라 <ul> 의 자식 노드인 <li> 와 <b> 같이 더 깊은 곳에 있는 중첩 요소도 <body>

의 후손 노드가 됩니다.

childNodes 컬렉션은 텍스트 노드를 포함한 모든 자식 노드를 담고 있습니다.

 

아래 예시를 실행하면 document.body의 자식노드가 출력됩니다.

<html>
<body>
  <div>시작</div>

  <ul>
    <li>항목</li>
  </ul>

  <div>끝</div>

  <script>
    for (let i = 0; i < document.body.childNodes.length; i++) {
      alert( document.body.childNodes[i] ); // Text, DIV, Text, UL, ... , SCRIPT
    }
  </script>
  ...추가 내용...
</body>
</html>

예시를 실행하면 흥미로운 점이 하나 발견됩니다. 마지막에 <script>가 출력됩니다.

<script> 아래 더 많은 내용(…추가 내용…)이 있지만, 스크립트 실행 시점엔 브라우저가 추가 내용은 읽지 못한 상태이기 때문에 스크립트 역시 추가 내용을 보지 못해서 이런 결과가 나타났습니다.

 


firstChild, lastChild 

첫 번째 마지막 노드에 빠르게 접근할 수 있는 방법!

 

자식노드가 존재할 때 아래 비교문은 항상 참이 됩니다.

elem.childNodes[0] === elem.firstChild
elem.childNodes[elem.childNodes.length - 1] === elem.lastChild

참고로 자식 노드의 존재 여부를 검사할 땐 함수 elem.hasChildNodes() 를 사용할 수 있습니다.

 


DOM 컬렉션

위에서 살펴본 childNodes 는 마치 배열 같아 보입니다. 하지만 childNodes는 배열이 아닌

반복 가능한 (iterable) 유사 배열 객체인 컬렉션(collection) 입니다.

 

childNodes는 컬렉션이기 때문에 아래와 같은 특징을 가집니다.

 

1. for .. of 를 사용할 수 있다.

for (let node of document.body.childNodes) {
  alert(node); // 컬렉션 내의 모든 노드를 보여줍니다.
}

 

 

이터러블이기 때문에 Symbol.iterator 프로퍼티가 구현이 되어있어 for..of를 사용하는 것이 가능합니다.

 

2. 배열이 아니기 때문에 배열 메서드를 사용할 수 없습니다.

alert(document.body.childNodes.filter); // undefined (filter 메서드가 없습니다.)

 

 

배열이 아니어서 배열 메서드를 사용할 순 없지만 Array.from 을 이용해 "진짜" 배열로 만들어줄 수 있습니다.

alert( Array.from(document.body.childNodes).filter ); // function

 

! 컬렉션에 for..in 반복문을 사용하지마세요 !

더보기

컬렉션은 for..of를 통해 순회할 수 있습니다. 그런데 가끔 for..in 을 사용하려는 사람들이 있죠.

 

for..in 은 절대 사용하지 마세요. for..in 반복문은 객체의 모든 열거 가능한 프로퍼티를 순회합니다.

컬렉션에는 거의 사용되지 않는 '추가' 프로퍼티가 있는데, 이 프로퍼티까지 순회대상에 포함되길 원치 않으실 거니까요.

<body>
<script>
  // 0, 1, length, item, values 등 불필요한 프로퍼티까지도 출력됩니다.
  for (let prop in document.body.childNodes) alert(prop);
</script>
</body>

형제와 부모노드

같은 부모를 가진 노드는 형제(sibling) 노드라고 부릅니다.

<head>, <body> 는 대표적인 형제 노드입니다.

<html>
  <head>...</head><body>...</body>
</html>
  • <head> 는 <body>의 이전(previous) 또는 좌측(left) 노드입니다.
  • <body> 는 <head>의 다음(next) 또는 우측(right) 노드입니다.

다음 형제 노드에 대한 정보는 nextSibling, 이전 형제 노드에 대한 정보는 previousSibling 프로퍼티에서 찾을 수 있습니다.

부모 노드에 대한 정보는 parentNode 프로퍼티를 이용해 참조할 수 있습니다.

// <body>의 부모 노드는 <html>입니다
alert( document.body.parentNode === document.documentElement ); // true

// <head>의 다음 형제 노드는 <body>입니다.
alert( document.head.nextSibling ); // HTMLBodyElement

// <body>의 이전 형제 노드는 <head>입니다.
alert( document.body.previousSibling ); // HTMLHeadElement

요소 간 이동

지금까지 언급한 탐색 관련 프로퍼티는 모든 종류의 노드를 참조합니다.

childNodes 를 이용하면 텍스트, 요소, 심지어 주석 노드까지 참조할 수 있습니다.

하지만 실무에서는 텍스트, 주석 노드에 대해서 거의 다루지 않습니다. 웹페이지를 구성하는 태그의 분신인

요소 노드를 조작하는 작업이 대다수이기 때문입니다.

 

이런 실제 상황을 토대로 DOM 요소 노드 탐색이 어떻게 이루어지는지 알아보겠습니다.

위 그림속 관계가 맨 처음 그림과 다른 점은 element 가 추가 되었다는 점입니다.

  • children 프로퍼티는 해당 요소의 자식 노드 중 요소 노드만 가리킵니다.
  • firstElementChild와 lastElementChild 프로퍼티는 각각 첫 번째 자식 요소 노드와 마지막 자식 요소 노드를 가리킵니다.
  • previousElementSibling과 nextElementSibling은 형제 요소 노드를 가리킵니다.
  • parentElement 는 부모 요소 노드를 가리킵니다.

 

 

 

 


요약

  • 탐색 프로퍼티를 사용하면 이웃 노드로 바로 이동할 수 있습니다.
  • 탐색 프로퍼티는 다음 두개 집합으로 나눠집니다.
  • 모든 노드에 적용 가능한 parentNode, childNodes, firstChild, lastChild, previousSibling, nextSibling
  • 요소 노드에 적용 가능한 parentElement, children, firstElementChild, lastElementChild, previousElementSibling, nextElementSibling