본문 바로가기

카테고리 없음

주요 노드 프로퍼티

 

 

주요 노드 프로퍼티

 

ko.javascript.info

DOM 노드 클래스

DOM 노드는 종류에 따라 각각 다른 프로퍼티를 지원합니다. 태그 <a>에 대응하는 요소 노드엔 링크 관련된 프로퍼티를, <input>에 대응하는 요소 노드엔 입력 관련 프로퍼티를 제공하죠. 텍스트 노드는 요소 노드와 다른 프로퍼티를 지원합니다. 그런데 모든 DOM 노드는 공통 조상으로부터 만들어지기 때문에 노드 종류는 다르지만, 모든 DOM 노드는 공통된 프로퍼티와 메서드를 지원합니다. DOM 노드는 종류에 따라 대응하는 내장 클래스가 다릅니다. 계층 구조 꼭대기엔 EventTarget이 있는데, Node는 EventTarget을, 다른 DOM 노드들은 Node 클래스를 상속 받습니다. 

 

 

각 클래스는 다음과 같은 특징을 지닙니다.

 

- EventTarget: 루트에 있는 추상(abstract) 클래스로, 이 클래스에 대응하는 객체는 실제로 만들어지지 않습니다. EventTarget가 모든 DOM 노드에 베이스에 있기 때문에 DOM 노드에서 '이벤트'를 사용할 수 있습니다. 

- Node: 역시 추상 클래스로 DOM 노드의 베이스 역할을 합니다. getter 역할을 하는 parentNode, nextSibling, childNodes등의 주요 트리 탐색 기능을 제공합니다. Node 클래스의 객체는 절대 생성되지 않습니다. 하지만 이 클래스를 상속받는 클래스는 여럿 있습니다. 텍스트 노드를 위한 Text 클래스와 요소 노드를 위한 Element 클래스, 주석 노드를 위한 Comment 클래스는 Node 클래스를 상속받습니다.

- Elements: DOM 요소를 위한 베이스 클래스이다. nextElementSibling, children이나 getElementsByTagName, querySelector 같이 요소 전용 탐색을 토와주는 프로퍼티나 메서드가 이를 기반으로 합니다. 브라우저는 HTML 뿐만 아니라 XML, SVG도 지원하는데 Element 클래스는 이와관련된 SVGElement, XMLElement, HTMLElement 클래스의 베이스 역할을 합니다. 

- HTMLElement: HTML 요소 노드의 베이스 역할을 하는 클래스입니다. 아래 나열한 클래스들은 실제 HTML 요소에 대응하고 HTMLElement를 상속받습니다. 

- HTMLInputElement: <input> 요소에 대응하는 클래스

- HTMLBodyElement: <body> 요소에 대응하는 클래스

- HTMLAnchorElement: <a> 요소에 대응하는 클래스

- 이외에도 다른 클래스가 많은데, 각 태그에 해당하는 클래스는 고유한 프로퍼티와 메서드를 지원합니다. 

 

이렇게 특정 노드에서 사용할 수 있는 프로퍼티와 메서드는 상속을 기반으로 결정됩니다. <input> 요소에 대응하는 DOM 객체를 예로 들어봅시다. 이 객체는 HTMLInputElement 클래스를 기반으로 만들어집니다. 객체엔 아래에 나열한 클래스에서 상속받은 프로퍼티와 메서드가 있을 겁니다. 

 

- HTMLInputElement: 입력 관련 프로퍼티를 제공하는 클래스

- HTMLElement: HTML 요소 메서드와 getter, setter를 제공하는 클래스

- Element: 요소 노드 메서드를 제공하는 클래스

- Node: 공통 DOM 노드 프로퍼티를 제공하는 클래스

- EventTarget: 이벤트 관련 기능을 제공하는 클래스

- Object: hasOwnProperty 같이 '일반 객체' 메서드를 제공하는 클래스

 

우리는 앞서 객체는 constructor 프로퍼티를 가진다는 걸 배운 바 있습니다. 이런 특징을 이용하면 DOM 노드 클래스 이름을 확인할 수 있습니다. constructor 프로퍼티는 클래스 생성자를 참조하고 이름은 constructor.name에 저장되어 있다는 점을 이용하면 되죠.

 

alert( document.body.constructor.name ); // HTMLBodyElement

 

toString을 사용해도됩니다.

 

document.body.toString() // [object HTMLBodyElement]

 

상속 여부는 instanceof를 사용해 확인할 수 있습니다.

 

alert( document.body instanceof HTMLBodyElement ); // true
alert( document.body instanceof HTMLElement ); // true
alert( document.body instanceof Element ); // true
alert( document.body instanceof Node ); // true
alert( document.body instanceof EventTarget ); // true

 

지금까지 살펴본 바와 같이 DOM 노드는 프로토타입을 기반으로 상속 관계를 갖는 일반 자바스크립트 객체입니다.브라우저 콘솔에 console.dir(elem)을 입력하면 이런 관계를 쉽게 확인할 수 있습니다. HTMLElement.prototype, Element.prototype등이 콘솔에 출력될 것입니다.

 

'nodeType' 프로퍼티

nodeType 프로퍼티는 DOM 노드의 '타입'을 알아내고자 할 때 쓰이는 구식 프로퍼티입니다. 각 노드 타입은 상숫값을 가집니다.

- elem.nodeType == 1 (요소노드)

- elem.nodeType == 3 (텍스트 노드)

- elem.nodeType == 9 (문서 객체)

 

<body>
  <script>
  let elem = document.body;

  // 타입을 알아봅시다.
  alert(elem.nodeType); // 1 => 요소 노드

  // 첫 번째 자식 노드
  alert(elem.firstChild.nodeType); // 3 => 텍스트 노드

  // 문서 객체의 타입 확인
  alert( document.nodeType ); // 9 => 문서 객체
  </script>
</body>

모던 자바스크립트에선 노드의 타입을 instanceof나 클래스 기반의 테스트를 이용해 확인하는데, 가끔은 nodeType을 쓰는게 간단할 때도 있습니다. nodeType은 타입 확인 하는데만 쓸 수 있고 바꾸지는 못합니다.

 

nodeName과 tagName으로 태그 이름 확인하기

nodeName이나 tagName 프로퍼티를 사용하면 DOM 노드의 태그 이름을 알아낼 수 있습니다. 

 

alert( document.body.nodeName ); // BODY
alert( document.body.tagName ); // BODY

 

그럼 tagName과 nodeName의 차이는 없는 걸까요?

물론 있습니다. 미묘하지만 이름에서 그 차이를 유추할 수 있습니다. 

- tagName 프로퍼티는 요소 노드에만 존재합니다. 

- nodeName은 모든 Node에 있습니다. 

  - 요소노드를 대상으로 호출하면 tagName과 같은 역할을 합니다.

  - 텍스트 노드, 주석 노드 등에선 노드 타입을 나타내는 문자열을 반환합니다.

 

nodeName은 모든 노드에서 지원되지만, tagName은 Element 클래스로부터 유래되었기 때문에 요소 노드에서만 지원됩니다. 

 

document와 주석 노드를 사용해 tagNaem과 nodeName의 차이점을 확인해 봅시다.

 

<body><!-- 주석 -->

  <script>
    // 주석 노드를 대상으로 두 프로퍼티 비교
    alert( document.body.firstChild.tagName ); // undefined (요소가 아님)
    alert( document.body.firstChild.nodeName ); // #comment

    // 문서 노드를 대상으로 두 프로퍼티 비교
    alert( document.tagName ); // undefined (요소가 아님)
    alert( document.nodeName ); // #document
  </script>
</body>

 

요소 노드만 다루고 있다면 tagName과 nodeName에는 차이가 없으므로 둘 다 사용할 수 있습니다.

innerHTML로 내용 조작하기

innerHTML 프로퍼티를 사용하면 요소 안의 HTML을 문자열 형태로 받아올 수 있습니다. 요소 안 HTML을 수정하는 것도 가능합니다. innerHTML은 페이지를 수정하는데 쓰이는 강력한 방법의 하나입니다. document.body 안의 내용을 출력하고 완전히 바꾸는 예시를 살펴봅시다.

 

<body>
  <p>p 태그</p>
  <div>div 태그</div>

  <script>
    alert( document.body.innerHTML ); // 내용 읽기
    document.body.innerHTML = '새로운 BODY!'; // 교체
  </script>

</body>

 

문법이 틀린 HTML을 넣으면 브라우저가 자동으로 고쳐줍니다.

innerHTML을 사용해 문서에 <script> 태그를 삽입하면 해당 태그는 HTML의 일부가 되긴 하지만 실행은 되지 않습니다.

 

innerHTML += 사용시 주의점

elem.innerHTML += "추가 html"을 사용하면 요소에 HTML을 추가할 수 있습니다. 아래와 같이 말이죠.

 

chatDiv.innerHTML += "<div>안녕하세요<img src='smile.gif'/> !</div>";
chatDiv.innerHTML += "잘 지내죠?";

 

그런데 innerHTML += 은 추가가 아니라 내용을 덮어쓰기 때문에 주의해서 사용해야 합니다. 기술적으로 아래 두 줄의 코드는 동일한 역할을 합니다.

 

elem.innerHTML += "...";
// 위 코드는 아래 코드의 축약 버전입니다.
elem.innerHTML = elem.innerHTML + "..."

 

즉, innerHTML += 는 아래와 같은 역할을 합니다.

1. 기존 내용 삭제

2. 기존 내용과 새로운 내용을 합친 새로운 내용을 씀

 

중요한 점은 기존의 내용이 완전히 삭제 된 후 밑바닥부터 다시 내용이 쓰여지기 때문에 이미지 등의 리소스가 전부 다시 로딩됩니다. 어딘가에 이런 리소스들을 캐싱해 놓았다면 좋았을 것이라는 생각이 드는 순간이네요. chatDiv에 텍스트와 이미지가 많이 있었다면 내용을 다시 불러올 때 버벅임이 생기는 걸 눈으로 확인할 수 있습니다. 이 외에도 innerHTML += 은 여러 부작용이 있습니다. 기존에 있던 텍스트를 마우스로 드래그한 상황이라면 내용을 다시 써야 하기 때문에 드래그가 해제될 것입니다. <input> 태그에서 사용자가 입력한 값이 사라지기도 하죠. 부작용 사례는 다양합니다. 

 

outerHTML로 요소의 전체 HTML 보기

outerHTML 프로퍼티엔 요소 전체 HTML이 담겨있습니다. outerHTML은 innerHTML에 요소 자체를 더한 것이라고 생각하시면 됩니다. 예시를 봅시다.

 

<div id="elem">Hello <b>World</b></div>

<script>
  alert(elem.outerHTML); // <div id="elem">Hello <b>World</b></div>
</script>

 

innerHTML과 달리 outerHTML을 사용해서 HTML을 쏠땐 요소 자체가 바뀌지 않습니다. 대신 outerHTML은 DOM 안의 요소를 교체합니다.

 

<div>Hello, world!</div>

<script>
  let div = document.querySelector('div');

  // div.outerHTML를 사용해 <p>...</p>로 교체
  div.outerHTML = '<p>새로운 요소</p>'; // (*)

  // 어! div가 그대로네요!
  alert(div.outerHTML); // <div>Hello, world!</div> (**)
</script>

 

위 예제에서 뭔가 이상합니다. (*)로 표시한 줄에서 div를 <p>새로운 요소</p>로 교체했기 때문에 예시를 실행하면 의도한 대로 문서(DOM)에 <div>가 아닌 새로운 내용이 보입니다. 그런데 (**)에서 기존의 div를 출력하네요.

 

이러한 결과가 나타난 이유는 outerHTML에 하는 할당 연산이 DOM 요소(outerHTML 연산의 대상으로, 위 예시에선 변수 div)를 수정하지 않기 때문입니다. 할당 연산은 요소를 DOM에서 제거하고 새로운 HTML 조각을 넣습니다. 즉, div.outerHTML=...는 아래와 같은 일을 합니다. 

 

- 문서에서 div를 삭제

- 새로운 HTML 조각인 <p>A new element</p>을 삭제 후 생긴 공간에 삽입

- div엔 여전히 기존 값이 저장되어 있고 새로운 HTML 조각은 어디에도 저장되어 있지 않음

 

outerHTML의 이런 동작 방식 떄문에 outerHTML을 사용할 땐 실수할 여지가 많습니다. div.outerHTML을 수정한 후 div에 새로운 내용이 들어갔다고 착각하며 작업하는 경우가 많습니다. 정리하면  innerHTML은 div를 수정하지만 outerHTML은 div를 수정하지 않습니다. 그렇기 때문에 elem.outerHTML에 무언가를 쓸 때는 elem이 수정되지 않는다는 점을 꼭 명심하고 있어야 합니다. 할당받은 HTML은 elem이 있던 공간에 들어갑니다. 새롭게 만들어진 요소를 참조하려면 DOM 쿼리 메서드를 사용합시다. 

 

nodeValue / data로 텍스트 노드 내용 조작하기

innerHTML 프로퍼티는 요소 노드에만 사용할 수 있습니다. 텍스트 노드 같은 다른 타입의 노드에는 innerHTML과 유사한 역할을 해주는 프로퍼티인 nodeValue와 data를 사용해야 합니다. 이 두 프로퍼티는 아주 유사하고, 실무에서도 구분 없이 쓰긴 하지만 명세서상에 작은 차이가 있긴 합니다만 data가 좀 더 짧기 떄문에 여기선 data를 사용해보겠습니다.

 

<body>
  안녕하세요.
  <!-- 주석 -->
  <script>
    let text = document.body.firstChild;
    alert(text.data); // 안녕하세요.

    let comment = text.nextSibling;
    alert(comment.data); // 주석
  </script>
</body>

 

텍스트 노드의 내용을 읽거나 수정하는일은 일어날 법 한데 주석 노드는 왜 이런 기능이 필요한건지 의문입니다. 개발자들은 종종 아래와 같은 방식으로 정보나 지시사항을 HTML에 삽입합니다.

 

<!-- if isAdmin -->
  <div>관리자로 로그인하였습니다!</div>
<!-- /if -->

 

이럴 때 data 프로퍼티 기능을 사용해 주석 노드의 내용을 읽고 삽입된 지시사항을 처리하면 유용합니다. 

 

textContent로 순수한 텍스트만

textContent를 사용하면 요소 내의 텍스트에 접근할 수 있습니다. <태그>는 제외하고 오로지 텍스트만 추출할 수 있죠.

 

<div id="news">
  <h1>주요 뉴스!</h1>
  <p>화성인이 지구를 침공하였습니다!</p>
</div>

<script>
  // 주요 뉴스! 화성인이 지구를 침공하였습니다!
  alert(news.textContent);
</script>

 

위 코드에서 원래부터 <태그>가 없었던 것처럼 텍스트만 반환되는 것을 확인할 수 있습니다. 그런데 실무에서는 텍스트 읽기를 단독으로 쓰는 경우는 흔치 않습니다.  textContent를 사용하면 텍스트를 '안전한 방법'으로 쓸 수 있기 때문에 실무에선 textContent를 쓰기 용으로 유용하게 사용합니다.

 

사용자가 입력한 임의의 문자열을 다시 출력해주는 경우를 생각해봅시다. 

- innerHTML을 사용하면 사용자가 입력한 문자열이 "HTML 형태로" 태그와 함께 저장됩니다. 

- textContent를 사용하면 사용자가 입력한 문자열이 '순수 텍스트 형태'로 저장되기 때문에 태그를 구성하는 특수 문자들이 문자열로 처리됩니다. 

 

hidden 프로퍼티

hidden 속성과 hidden 프로퍼티는 요소를 보여줄지 말지 지정할 때 사용할 수 있습니다. hidden은 HTML 안에서 쓸 수도 있고 자바스크립트에서도 쓸 수 있습니다.

 

<div>아래 두 div를 숨겨봅시다.</div>

<div hidden>HTML의 hidden 속성 사용하기</div>

<div id="elem">자바스크립트의 hidden 프로퍼티 사용하기</div>

<script>
  elem.hidden = true;
</script>

hidden은 기술적으로 style="display: none"과 동일합니다.

 

기타 프로퍼티

- value - <input>과 <select>. <textarea>의 값이 저장됩니다. 대응하는 클래스는 HTMLInputElement, HTMLSelectElement등입니다.

- href - <a href="..."> 의 href 속성값이 저장됩니다. 대응하는 클래스는 HTMLAnchorElement입니다.

- id - id속성 값이 저장됩니다. 모든 요소 노드에서 사용할  수 있으며, 대응하는 클래스는 HTMLElement입니다. 

 

<input type="text" id="elem" value="value">

<script>
  alert(elem.type); // "text"
  alert(elem.id); // "elem"
  alert(elem.value); // value
</script>

 

대부분의 표준 HTML 속성은 그에 대응하는 DOM 프로퍼티를 가지고 있는데, 위 예시와 같은 방식으로 프로퍼티에 접근할 수 있습니다. 특정 클래스에서 지원하는 프로퍼티 전체를 보고 싶다면 명세서를 따르면 됩니다. 

 

명세서를 읽지 않고 개발자 도구의 콘솔창에 console.dir(elem)을 입력하면 해당 요소에서 지원하는 프로퍼티 목록을 빠르게 확인할 수 있습니다. 개발자 도구의 Elements 패널의 하위 패널 중 'Properties'를 선택해도 동일한 목록을 확인할 수 있습니다.