본문 바로가기

CS/모던 자바스크립트 Deep Dive

39장 DOM(2)

반응형

2023년 5월 2일 700p~714p

 

39.3 노드 탐색

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

Node.prototype 이 제공하는 프로퍼티

  • parentNode : 부모 노드 탐색, (부모 노드는 텍스트 노드가 될 수 X)
  • previousSibling : 이전 형제 노드 탐색하여 반환, 요소 노드 또는 텍스트 노드를 반환
  • firstChild : 첫 번째 자식 노드 반환 , 요소 노드 또 는 텍스트 노드
  • childNodes : 자식 노드를 모두 탐색 하여 NodeList로 반환, 텍스트 노드 포함 될 수 있음

Element.prototype 이 제공하는 프로퍼티

  • previousElementSibling : 이전 형제 노드 반환, 요소 노드만 반환
  • nextElementSibling : 자신의 다음 형제 요소 노드 반환, 요소 노드만 반환
  • children : 자식 노드 중에서 요소 노드만 모두 탐색하여 HTMLCollection 으로 반환 (텍스트 노드 포함 x)

노드 탐색 프로퍼티는 모두 접근자 프로퍼티다. 단, 노드 탐색 프로퍼티는 setter없이 getter만 존재하여 참조만 가능한 읽기 전용 접근자 프로퍼티다.

39.3.1 공백 텍스트 노드

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

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li class="apple">Apple</li>
      <li class="banana">Banana</li>
      <li class="orange">Orange</li>
    </ul>
  </body>
</html>

노드를 탐색할 때 공백 문자가 생성한 공백 텍스트 노드에 주의해야 한다.

39.3.2 자식 노드 탐색

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

  • Node.prototype.childNodes
    자식 노드를 모두 탐색하여 NodeList에 담아 반환한다. 요소 노드와 텍스트 노드를 포함한다.
  • Element.prototype.children
    자식 노드 중에서 요소 노드만 모두 탐색하여 HTMLCollection에 담아 반환한다. 텍스트 노드는 포함하지 않는다
  • Node.prototype.firstChild
    첫 번째 자식 노드를 반환하며, 텍스트 노드이거나 요소 노드이다.
  • Node.prototype.lastChild
    마지막 자식 노드를 반환하며, 텍스트 노드이거나 요소 노드이다.
  • Element.prototype.firstElementChild
    첫 번째 자식 노드이며, 요소 노드만 반환한다.
  • Element.prototype.lastElementChild
    마지막 자식 요소 노드이며, 요소 노드만 반환한다.
<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li class="apple">Apple</li>
      <li class="banana">Banana</li>
      <li class="orange">Orange</li>
    </ul>
  </body>
  <script>
    // 노드 탐색의 기점이 되는 #fruits 요소 노드를 취득한다.
    const $fruits = document.getElementById('fruits');

    // #fruits 요소의 모든 자식 노드를 탐색한다.
    // childNodes 프로퍼티가 반환한 NodeList에는 요소 노드뿐만 아니라 텍스트 노드도 포함되어 있다.
    console.log($fruits.childNodes);
    // NodeList(7) [text, li.apple, text, li.banana, text, li.orange, text]

    // #fruits 요소의 모든 자식 노드를 탐색한다.
    // children 프로퍼티가 반환한 HTMLCollection에는 요소 노드만 포함되어 있다.
    console.log($fruits.children);
    // HTMLCollection(3) [li.apple, li.banana, li.orange]

    // #fruits 요소의 첫 번째 자식 노드를 탐색한다.
    // firstChild 프로퍼티는 텍스트 노드를 반환할 수도 있다.
    console.log($fruits.firstChild); // #text

    // #fruits 요소의 마지막 자식 노드를 탐색한다.
    // lastChild 프로퍼티는 텍스트 노드를 반환할 수도 있다.
    console.log($fruits.lastChild); // #text

    // #fruits 요소의 첫 번째 자식 노드를 탐색한다.
    // firstElementChild 프로퍼티는 요소 노드만 반환한다.
    console.log($fruits.firstElementChild); // li.apple

    // #fruits 요소의 마지막 자식 노드를 탐색한다.
    // lastElementChild 프로퍼티는 요소 노드만 반환한다.
    console.log($fruits.lastElementChild); // li.orange
  </script>
</html>

39.3.3 자식 노드 존재 확인

자식 노드가 존재하는지 확인하려면 Node.prototype.hasChildNodes 메서드를 사용한다. 자식 노드가 존재하면 true, 아니면 false를 반환한다. 자식 노드에 요소 노드와 텍스트 노드를 포함하여 확인한다.

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
    </ul>
  </body>
  <script>
    // 노드 탐색의 기점이 되는 #fruits 요소 노드를 취득한다.
    const $fruits = document.getElementById('fruits');

    // #fruits 요소에 자식 노드가 존재하는지 확인한다.
    // hasChildNodes 메서드는 텍스트 노드를 포함하여 자식 노드의 존재를 확인한다.
    console.log($fruits.hasChildNodes()); // true
  </script>
</html>

텍스트 노드를 제외한 요소 노드만 확인하고 싶은 경우 children.length 또는 Element의 childElementCount 프로퍼티를 사용한다.

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
    </ul>
  </body>
  <script>
    // 노드 탐색의 기점이 되는 #fruits 요소 노드를 취득한다.
    const $fruits = document.getElementById('fruits');

    // hasChildNodes 메서드는 텍스트 노드를 포함하여 자식 노드의 존재를 확인한다.
    console.log($fruits.hasChildNodes()); // true

    // 자식 노드 중에 텍스트 노드가 아닌 요소 노드가 존재하는지는 확인한다.
    console.log(!!$fruits.children.length); // 0 -> false
    // 자식 노드 중에 텍스트 노드가 아닌 요소 노드가 존재하는지는 확인한다.
    console.log(!!$fruits.childElementCount); // 0 -> false
  </script>
</html>

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

요소 노드의 텍스트 노드는 firstChild 프로퍼티로 접근할 수 있다. 반환값은 텍스트 노드이거나 요소 노드일 수 있다.

<!DOCTYPE html>
<html>
<body>
  <div id="foo">Hello</div>
  <script>
    // 요소 노드의 텍스트 노드는 firstChild 프로퍼티로 접근할 수 있다.
    console.log(document.getElementById('foo').firstChild); // #text
  </script>
</body>
</html>

39.3.5 부모 노드 탐색

부모 노드를 탐색하려면 Node.prototype.parentNode 프로퍼티를 사용한다. 부모 노드가 텍스트 노드인 경우는 없기 때문에 반환 값은 요소 노드이다.

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li class="apple">Apple</li>
      <li class="banana">Banana</li>
      <li class="orange">Orange</li>
    </ul>
  </body>
  <script>
    // 노드 탐색의 기점이 되는 .banana 요소 노드를 취득한다.
    const $banana = document.querySelector('.banana');

    // .banana 요소 노드의 부모 노드를 탐색한다.
    console.log($banana.parentNode); // ul#fruits
  </script>
</html>

39.3.6 형제 노드 탐색

부모 노드가 같은 형제 노드를 탐색하려면 다음과 같은 노드 탐색 프로퍼티를 사용한다. 텍스트 노드 또는 요소 노드만 반환한다. 어트리뷰트는 요소 노드와 연결되어 있지만 부모 노드가 같은 형제 노드는 아니기 때문에 반환되지 않는다.

  • Node.prototype.previousSibling
    부모 노드가 같은 형제 노드 중에서 요소 노드와 텍스트 노드를 포함하여 자신의 이전 형제 노드를 반환한다
  • Node.prototype.nextSibling
    부모 노드가 같은 형제 노드 중에서 요소 노드와 텍스트 노드를 포함하여 자신의 이후 형제 노드를 반환한다
  • Element.prototype.previousElementSibling
    부모 노드가 같은 형제 노드 중에서 요소 노드만 포함하여 자신의 이전 형제 노드를 반환한다
  • Element.prototype.nextElementSibling
    부모 노드가 같은 형제 노드 중에서 요소 노드만 포함하여 자신의 이후 형제 노드를 반환한다
<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li class="apple">Apple</li>
      <li class="banana">Banana</li>
      <li class="orange">Orange</li>
    </ul>
  </body>
  <script>
    // 노드 탐색의 기점이 되는 #fruits 요소 노드를 취득한다.
    const $fruits = document.getElementById('fruits');

    // #fruits 요소의 첫 번째 자식 노드를 탐색한다.
    // firstChild 프로퍼티는 요소 노드뿐만 아니라 텍스트 노드를 반환할 수도 있다.
    const { firstChild } = $fruits;
    console.log(firstChild); // #text

    // #fruits 요소의 첫 번째 자식 노드(텍스트 노드)의 다음 형제 노드를 탐색한다.
    // nextSibling 프로퍼티는 요소 노드뿐만 아니라 텍스트 노드를 반환할 수도 있다.
    const { nextSibling } = firstChild;
    console.log(nextSibling); // li.apple

    // li.apple 요소의 이전 형제 노드를 탐색한다.
    // previousSibling 프로퍼티는 요소 노드뿐만 아니라 텍스트 노드를 반환할 수도 있다.
    const { previousSibling } = nextSibling;
    console.log(previousSibling); // #text

    // #fruits 요소의 첫 번째 자식 요소 노드를 탐색한다.
    // firstElementChild 프로퍼티는 요소 노드만 반환한다.
    const { firstElementChild } = $fruits;
    console.log(firstElementChild); // li.apple

    // #fruits 요소의 첫 번째 자식 요소 노드(li.apple)의 다음 형제 노드를 탐색한다.
    // nextElementSibling 프로퍼티는 요소 노드만 반환한다.
    const { nextElementSibling } = firstElementChild;
    console.log(nextElementSibling); // li.banana

    // li.banana 요소의 이전 형제 요소 노드를 탐색한다.
    // previousElementSibling 프로퍼티는 요소 노드만 반환한다.
    const { previousElementSibling } = nextElementSibling;
    console.log(previousElementSibling); // li.apple
  </script>
</html>

 

39.4 노드 정보 취득

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

  • Node.prototype.nodeType
    노드 객체의 타입을 나타내는 상수를 반환한다.
    요소 노드 : 1, 텍스트 노드 : 3, 문서 노드 : 9 반환
  • Node.prototype.nodeName
    노드의 이름을 문자열로 반환한다.
    요소 노드 : 태그 이름, 텍스트 노드 : #text, 문서 노드 : #document 반환
<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello</div>
  </body>
  <script>
    // 문서 노드의 노드 정보를 취득한다.
    console.log(document.nodeType); // 9
    console.log(document.nodeName); // #document

    // 요소 노드의 노드 정보를 취득한다.
    const $foo = document.getElementById('foo');
    console.log($foo.nodeType); // 1
    console.log($foo.nodeName); // DIV

    // 텍스트 노드의 노드 정보를 취득한다.
    const $textNode = $foo.firstChild;
    console.log($textNode.nodeType); // 3
    console.log($textNode.nodeName); // #text
</script>
</html>

 

39.5 요소 노드의 텍스트 조작

39.5.1 nodeValue

Node.prototype.nodeValue 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티다. 즉, nodeValue 프로퍼티는 참조와 할당 모두 가능하다.

만약 텍스트 노드가 아닌 노드에 nodeValue 프로퍼티를 참조하면 null을 반환한다.

<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello</div>
  </body>
  <script>
    // 문서 노드의 nodeValue 프로퍼티를 참조한다.
    console.log(document.nodeValue); // null

    // 요소 노드의 nodeValue 프로퍼티를 참조한다.
    const $foo = document.getElementById('foo');
    console.log($foo.nodeValue); // null

    // 텍스트 노드의 nodeValue 프로퍼티를 참조한다.
    const $textNode = $foo.firstChild;
    console.log($textNode.nodeValue); // Hello
  </script>
</html>

텍스트 노드의 nodeValue 프로퍼티에 값을 할당하면 텍스트 노드의 값, 즉 텍스트를 변경할 수 있다.

<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello</div>
  </body>
  <script>
    // 1. #foo 요소 노드의 자식 노드인 텍스트 노드를 취득한다.
    const $textNode = document.getElementById('foo').firstChild;

    // 2. nodeValue 프로퍼티를 사용하여 텍스트 노드의 값을 변경한다.
    $textNode.nodeValue = 'World';

    console.log($textNode.nodeValue); // World
  </script>
</html>

39.5.2 textContent

Node.prototype.textContent 프로퍼티도 setter와 getter 모두 존재하는 접근자 프로퍼티로서 요소 노드의 텍스트와 모든 자손 노드의 텍스트를 모두 취득하거나 변경한다. 즉, 요소 노드의 childNodes 프로퍼티가 반환한 모든 노드들의 텍스트 노드의 값을 반환하고, 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>

nodeValue 프로퍼티를 참조하여도 텍스트를 취득할 수 있지만 코드가 더 복잡하다.

<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello <span>world!</span></div>
  </body>
  <script>
    // #foo 요소 노드는 텍스트 노드가 아니다.
    console.log(document.getElementById('foo').nodeValue); // null
    // #foo 요소 노드의 자식 노드인 텍스트 노드의 값을 취득한다.
    console.log(document.getElementById('foo').firstChild.nodeValue); // Hello
    // span 요소 노드의 자식 노드인 텍스트 노드의 값을 취득한다.
    console.log(document.getElementById('foo').lastChild.firstChild.nodeValue); // world!
  </script>
</html>

textContent 프로퍼티에 문자열을 할당하면 요소 노드의 모든 자식 노드가 제거되고 할당한 문자열이 텍스트로 추가된다. 이떄 HTML 마크업이 파싱되지 않는다.

<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello <span>world!</span></div>
  </body>
  <script>
    // #foo 요소 노드의 모든 자식 노드가 제거되고 할당한 문자열이 텍스트로 추가된다.
    // 이때 HTML 마크업이 파싱되지 않는다.
    document.getElementById('foo').textContent = 'Hi <span>there!</span>';
  </script>
</html>

textContent 프로퍼티와 유사한 동작을 하는 innerText 프로퍼티가 있다.

innerText 프로퍼티는 CSS에 순종적(display가 hidden일 경우 반환되지 않는다)이고, innerText 프로퍼티는 CSS를 고려해야하므로 textContent보다 느리기 때문에 사용하지 않는 것이 좋다.

반응형

'CS > 모던 자바스크립트 Deep Dive' 카테고리의 다른 글

39장 DOM(4)  (0) 2023.05.04
39장 DOM(3)  (0) 2023.05.03
39장 DOM(1)  (0) 2023.05.01
38장 브라우저의 렌더링 과정  (0) 2023.04.29
34장 이터러블  (0) 2023.04.28