먹고 기도하고 코딩하라

parentNode-parentElement 차이, children과 childNodes 차이 본문

Javascript

parentNode-parentElement 차이, children과 childNodes 차이

사과먹는사람 2021. 8. 15. 22:38
728x90
728x90

 

웹앱을 만들면서 DOM 요소를 조작하다 보면 어떤 요소의 부모 요소에 접근해야 하거나 자식 요소에 접근해야 하는 일이 생긴다. 그 때마다 parentNode를 쓰면 안 되고 parentElement를 쓰면 되고 하는 식으로 비슷해 보이는데 기묘하게 쓰임새가 다른 것들은 그냥 시도하면서 고개만 갸웃거렸는데 이번에 정리를 하려고 한다.

 

라고 하기 전에 먼저 노드와 엘리먼트 사이에 무슨 차이가 있는지 짚고 넘어가는 게 명확한 이해에 도움이 될 것 같다.

먼저 구글링을 해 보니 나처럼 아주 혼란스러워하는 스택오버플로우 질문자의 글이 보였고, 거기에 아주 좋은 답변이 추천도 많이 받아서 일부를 소개하고자 한다.

node는 DOM 계층구조에 속한 객체의 어떤 타입이든 가리킬 수 있는 이름입니다. node는 내장 DOM 엘리먼트(document, document.body)나 HTML의 특정 태그(input, p) 또는 텍스트 노드가 될 수도 있습니다. 그니까 간단히 얘기해서 node는 아무 DOM 객체나 될 수 있다는 거죠.
element는 node의 특정한 하나의 타입인데요. 노드에는 정말 많은 타입이 있습니다... (텍스트 노드, 주석 노드, document 노드 기타 등등) element는 HTML tag로 바로 특정할 수 있거나, id나 class 같은 속성을 가진 것들입니다. 각 노드들은 .nodeType이라는 프로퍼티를 갖고 있는데, 이게 바로 그 노드가 어떤 타입인지 말해 주죠. ELEMENT_NODE 타입은 nodeType 프로퍼티 값이 1인 특정 타입입니다.

Node의 여러 타입 중 하나가 element이며 element는 Node보다 더 작은 개념이라고 생각하면 될 듯하다.

 

자! 이제 부모 찾는 2가지 방법을 대조해 보자.

 

 

1-1. Node.parentNode

parentNode는 DOM 트리에서 특정한 노드(주로 현재 다루고 있는 노드)의 부모 노드를 반환하는데, 이 때 parentNode는 읽기 전용 노드가 된다. 평상시(즉, parent가 ELEMENT_NODE인 경우)에는 거의 parentElement와 똑같은 값을 반환하고는 한다.

Document와 DocumentFragment 노드는 부모 노드가 없으므로 parentNode에 접근하면 null 값이 반환된다.

 

1-2. Node.parentElement

parentElement는 노드의 부모 요소를 반환하는데, 부모가 없거나 부모가 DOM 요소가 아니라면 null을 반환한다. parentElement의 반환값은 언제나 DOM 요소이거나 null이다. parentNode가 부모가 요소인지 아닌지 따지지 않고 그냥 요소 노드를 반환해 준다면 parentElement의 경우에는 부모가 꼭 요소여야만 Element 형식으로 반환하고 그렇지 않으면 null을 반환한다는 것이다.

 

당최 이게 무슨 말인가? 부모가 Element가 아닐 수도 있단 말인가?

그렇다. 다음 예시를 보자.

 

드물지만, parentNode와 parentElement의 값이 다른 예시이다.

document.documentElement의 부모는 document인데, DOM 트리에는 document '요소'라는 건 없기 때문이다. 대신 document는 그냥 DOM 객체(document node)로서 존재한다. 이것은 HTML 문서 최상위 요소인 html에 적용해도 같은 결과가 나온다.

방금 위에서 소개한 스택오버플로우 답변글에는 nodeType별로 type-value이 적힌 표가 있는데, 여기서 1번은 ELEMENT_NODE라고 했으니, 분명 html.parentNode는 1번이 아닐 것이다. 정말 그런지 확인해 보자.

정말이다! html은 element이기 때문에 1번이지만, html.parentNode(#document)는 고유한 타입 DOCUMENT_NODE이기 때문에 9번으로 분류된다.

parentNode와 parentElement가 어떻게 다른지 살펴봤다. 평상시에는 그냥 아무거나 하나를 쓰면 되지만(뭘 쓰는 게 더 권장되는 스타일인지는 모르겠다), 부모가 element가 아닌 경우에는 섬세하게 다뤄줘야 한다.

 

 

2-1. childNodes

사실 parent보다 나를 더 많이 괴롭힌 건 children과 childNodes이다. (인생사처럼 말하게 되는 건 왜일까) childNodes부터 살펴보도록 하자.

 

childNodes는 parentNode와 비슷하게, 어떤 요소의 자식이 꼭 태그가 아니어도 다 품고 있다. 차이가 있다면 부모는 하나지만 자식은 여럿일 수 있기 때문에 컬렉션 형태(NodeList)로 주어진다는 것이다.

무슨 뜻인지 이해가 잘 안 된다. 예시를 살펴보도록 하자. dummy.html이라는 간단한 HTML 문서를 만들어 #wrap의 childNodes들을 찍어보자.

<html>
  <body>
    <div id="wrap">
      <!-- Comment -->
      <p>Hello World</p>
      You must be new here right?
    </div>
  </body>
</html>

childNodes의 결과로 NodeList가 나오게 된다.

그건 그렇고 이 결과를 납득할 수 있는가? 우리는 #text는 코드에 전혀 써넣지 않았는데 무려 2개나 들어가 있다.

원인은 다음과 같다. 요소 앞뒤로 공백이 있으면 이렇게 텍스트 노드가 childNodes로 끼어들어가기도 한다는 것이다. 우리가 전혀 의도하지 않았음에도 말이다. #text를 열어보면 NodeValue가 '\n'으로, 단순한 개행인 것을 알 수 있다.

추가적인 실험을 해 봤다. lastChild는 "You must be..."로, 텍스트노드인 3번이다. 그런데 firstChild인 #text도 텍스트노드인 3번이다. firstChild의 nextSibling은 주석인데, Comment Node는 8번이므로 제대로 잡아내고 있다.

 

 

2-2. children

children은 parentElement처럼 여러 자식들 중 요소 노드만 잡아서 자식으로 쳐서 컬렉션으로 만들어 반환한다. 우리가 흔히 생각하는 어떤 요소의 자식 요소들만 보려면 children을 써야 한다. 위의 예시를 다시 써서 children은 어떤 결과가 나오는지 확인해 보자.

p 태그 하나만 자식으로 나오는 모습이다. 주석, 텍스트 노드는 children에 포함되지 않았다.

DOM 탐색으로 나오는 것들은 모두! read-only이다. parent, children, sibling 할 것 없이 전부! 이것들을 바꾸려면 appendChild, removeChild 등의 메소드를 써야 한다. 

 

이와 마찬가지로 nextSibling, nextElementSibling 간의 관계, previousSibling, previousElementSibling 간의 관계, firstChild, firstElementChild 간의 관계, lastChild, lastElementChild 간의 관계도 위와 비슷하다. 

 

 

 

References

 

728x90
반응형
Comments