본문 바로가기

FE

1.3 DOM 탐색하기

모던 JavaScript 튜토리얼 파트 2. 브라우저: 문서, 이벤트, 인터페이스를 읽고 정리, 기록

 

브라우저 환경과 다양한 명세서

 

브라우저 환경과 다양한 명세서

 

ko.javascript.info

 

DOM에 수행되는 모든 연산은 document 객체에서 시작한다.

document 객체: DOM에 접근하기 위한 진입점

 

DOM의 노드 탐색


트리 상단: documentElement, body

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

<html> = document.documentElement
<body> = document.body
<head> = document.head

 

document.body 가 null 일 수도 있다.
스크립트를 읽는 도중 존재하지 않는 요소는 스크립트에서 접근할 수 없다.
브라우저가 아직 document.body를 읽지 못했기 때문에, <head> 안에 있는 스크립트에서는 document.body에 접근할 수 없다.
DOM에서의 null = ‘존재하지 않음’
<!-- 예시 -->
<html>

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

<body>

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

</body>
</html>

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

자식 노드 (child node, children)

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

  • ex) <head>와 <body>는 <html> 요소의 자식 노드이다.

후손 노드 (descendants)

중첩 관계에 있는 모든 요소를 의미한다.

  • ex) 자식 노드, 자식 노드의 모든 자식 노드 등

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

for (let i = 0; i < document.body.childNodes.length; i++) {
	// 모든 자식 노드 출력: Text, DIV, Text, UL, ... , SCRIPT
 	alert( document.body.childNodes[i] );
}

firstChild와 lastChild 프로퍼티를 이용하면 첫 번째, 마지막 자식 노드에 빠르게 접근할 수 있다.

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

elem.hasChildNodes(): 자식 노드의 존재 여부를 검사

DOM 컬렉션

위의 childNodes는 배열이 아니고 컬렉션(collection) 이다.

  • iterable 사용 - Symbol.iterator 프로퍼티가 구현되어 있다.
  1. for..of 사용 가능
for (let node of document.body.childNodes) {
	alert(node);
}
  1. 배열 메서드 사용 불가능
// undefined (filter 메서드가 없습니다.)
document.body.childeNodes.filter

// Array.from 으로 배열로 만들면 사용 가능
Array.from(document.body.childNodes).filter

 

DOM 컬렉션은 읽는 것만 가능하다. (읽기 전용)
DOM 컬렉션은 현재 상태를 반영한다.

참조하고 있는 도중에 새로운 노드가 추가되거나 삭제되면, 변경사항이 컬렉션에도 자동으로 반영된다.

 

컬렉션에 for..in 반복문을 사용하지 말아라. 대신 for..of 를 사용해라.
참조: https://jsdev.kr/t/for-in-vs-for-of/2938
- for..in 반복문 : 객체의 모든 열거 가능한 속성에 대해 반복
- for..of 반복문 : [Symbol.iterator] 속성을 가지는 컬렉션 전용
<body>
<script>
	// 0, 1, length, item, values 등 불필요한 프로퍼티까지도 출력된다.
	for (let prop in document.body.childNodes) {
		alert(prop);
	}
</script>
</body>

 


형제와 부모 노드

형재(sibling) 노드: 같은 부모를 가진 노드

  • ex) <head>, <body>
    • <body>는 <head>의 ‘다음(next)’ 혹은 '우측(right)'에 있는 형제 노드
    • <head>는 <body>의 ‘이전(previous)’ 혹은 '좌측(left)'에 있는 형제 노드
  • nextSibling: 다음 형제 노드에 대한 정보
  • previosSibling: 이전 형제 노드에 대한 정보
  • 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 요소 노드 탐색

  • DOM 노드 탐색에서 Element 라는 단어만 추가됨
  • children: 해당 자식 노드 중 요소 노드만을 가리킴

부모가 요소가 아니라면 parentElement는 어떻게 되는지?

parentNode: 종류에 상관없이 ‘부모 노드’ 반환

parentElement: ‘부모 요소 노드’ 반환

하지만..

document.documentElement.parentNode; // document
document.documentElement.parentElement; // null

반환값이 다른 이유?

⇒ documentElement의 부모는 document인데, document는 요소 노드가 아니기 때문

 


테이블 탐색하기

일부 DOM 요소 노드는 편의를 위해 기본 프로퍼티 외에 추가적인 프로퍼티를 지원

테이블 <table>

  1. table.rows 는 <tr> 요소를 담은 컬렉션을 참조
  2. table.caption/tHead/tFoot 은 각각 <caption>, <thead>, <tfoot> 요소를 참조
  3. table.tBodies 는 <tbody> 요소를 담은 컬렉션을 참조 (브라우저는 <tbody> 가 없더라고 <tbody> 노드를 DOM에 자동으로 추가한다.)
  4. <thead>, <tfoot>, <tbody> 요소는 rows 프로퍼티를 지원
  5. <tr> 요소는 다음 프로퍼티를 지원
    1. tr.cells 는 주어진 <tr> 안의 모든 <td>, <th> 을 담은 컬렉션을 반환
    2. tr.sectionRowIndex 는 주어진 <tr> 이 <thead>/<tbody>/<tfoot> 안쪽에서 몇 번째 줄에 위치하는지를 나타내는 인덱스 반환
    3. tr.rowIndex는 <table>내에서 해당 <tr>이 몇 번째 줄인 지를 나타내는 인덱스 반환
  6. <td>, <th> 요소는 다음 프로퍼티를 지원
    1. td.cellIndex 는 <td>나 <th>가 속한 <tr>에서 해당 셀이 몇 번째인지를 나타내는 인덱스를 반환

table 공식 명세서: tabular data