tech.junha.kr

노트/DOM

노드 (Node)

작성

1. DOM은 Node로 이루어진 트리다

  • DOM 이란, HTML을 나타내는 노드 객체들의 트리 구조를 말한다.
  • HTML 문서를 작성할 때, HTML 콘텐츠를 다른 HTML 콘텐츠 내에 캡슐화하게 되며, 브라우저는 HTML 문서를 해석해서 트리 형태로 구조화, 계층화된 노드들을 가지고 있는 DOM 을 생성한다.
  • DOM 목적: Javascript를 사용해서 HTML을 조작하기 위한 인터페이스를 제공

  • 💛 DOM을 이루는 모든 객체들은 Node 객체로부터 상속 받는다.

2. 노드의 유형 (nodeType)

  • HTML 문서를 다룰 때 마주치게 되는 일반적인 노드 유형이다.
for (var key in Node) {
  console.log(key, Node[key]);
}

// ELEMENT_NODE 1
// ATTRIBUTE_NODE 2
// TEXT_NODE 3
// CDATA_SECTION_NODE 4
// ENTITY_REFERENCE_NODE 5
// ENTITY_NODE 6
// PROCESSING_INSTRUCTION_NODE 7
// COMMENT_NODE 8
// DOCUMENT_NODE 9
// DOCUMENT_TYPE_NODE 10
// DOCUMENT_FRAGMENT_NODE 11
// NOTATION_NODE 12
// DOCUMENT_POSITION_DISCONNECTED 1
// DOCUMENT_POSITION_PRECEDING 2
// DOCUMENT_POSITION_FOLLOWING 4
// DOCUMENT_POSITION_CONTAINS 8
// DOCUMENT_POSITION_CONTAINED_BY 16
// DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC 32
  • 모두 대문자이며 _로 단어를 구분한다.
숫자코드nodeType인터페이스/생성자설명
1ELEMENT_NODEHTML*ELEMENT, HTMLBodyElement, <body>, <a>, <p>, <script>, <style>, <html>, <h1>속성 노드를 가질 수 있는 유일한 노드
2ATTRIBUTE_NODEclass = "active"모든 HTML요소의 속성은 속성 노드이며 엘리먼트 노드에 대한 정보를 가진다. 해당 요소 노드의 자식노드에는 포함되지 않는다.
3TEXT_NODEText(), html 문서 내의 텍스트 문자
9DOCUMENT_NODEwindow.document, HTMLDocument()HTML 문서 전체를 나타내는 노드
10DOCUMENT_TYPE_NODE<!DOCTYPE HTML>, DocumentType()
11DOCUMENT_FRAGMENT_NODEdocument.createDocumentFragment()

3. Node를 상속받은 하위 노드

  • DOM 트리의 모든 노드들은 Node로 부터 속성과 메서드를 상속받는다.
    • HTMLDocument > Document > Node > Object
    • HTMLElement > Element > Node > Object
    • Text > CharacterData > Node > Object
    • DocumentFragment > Node > Object
  • Node도 Javascript 생성자 함수이기 때문에 다른 객체들 처럼 Object.prototype를 상속받는다.

4. Node의 다양한 속성 및 메서드

Node.property

  • Node.childNodes
  • Node.firstChild
  • Node.lastChild
  • Node.nextSibling
  • Node.nodeName
  • Node.nodeType
  • Node.nodeValue
  • Node.parentNode
  • Node.previousSibling

Node.method()

  • Node.appendChild()
  • Node.cloneNode()
  • Node.compareDocumentPosition()
  • Node.contains()
  • Node.hasChildNodes()
  • Node.insertBefore()
  • Node.isEqualNode()
  • Node.removeChild()
  • Node.replaceChild()

하위 노드 인터페이스에서도 다양한 속성 및 메서드가 존재한다.

Document.method()

  • document.createElement()
  • document.createTextNode()

HTML*Element.property

  • innerHTML
  • outerHTML
  • textContent
  • innerText
  • outerText
  • firstElementChild
  • lastElementChild
  • nextElemmentChild
  • previousElementChild
  • children

HTMLElement.method()

  • insertAdjacentHTML()

5. 노드의 유형, 이름 식별

  • 모든 노드는 Node로 부터 상속받는 nodeType nodeName 속성을 가진다.
document.querySelector('a').nodeType // 1 Node.ELEMENT_NODE
document.querySelector('a').nodeName // A

6. 노드 값 가져오기

  • nodeValue 속성은 대부분 노드 유형에서는 null값을 가져온다.
  • 이 속성의 용도는 Text와 Comment노드에서 실제 텍스트 문자열을 추출하는데 초점을 맞추고 있다.
<a href="#">Hi</a>
document.doctype.nodeValue // null
document.querySelector('a').nodeValue // null

document.querySelector('a').firstChild.nodeValue // Hi

7,8. 노드 생성하기

  • 브라우저는 HTML문서를 초기 로딩할 때 노드를 생성을 처리한다.
  • 하지만 DOM API를 이용하면, HTML 로딩 이후에도 노드를 생성할 수 있다.

1) 메서드로 만들기

  • createElement()
  • createTextNode()
  • createAttribute()
  • createComment()
document.createElement('div') 
// <div></div>

document.createTextNode('Hi') 
// "HI"

2) 문자열로 만들기

  • innerHTML
  • outerHTML
  • textContent
  • insertAdjacentHTML()
<div id="A"></div>
<div id="B"></div>
<span id="C"></span>
<div id="D"></div>
<div id="E"></div>
document.getElementByID('A').innerHTML = '<strong>Hi</strong>'
document.getElementByID('B').outerHTML = '<div id="B" class="new">Da-ran!</div>'
document.getElementByID('C').textContent = 'dude'
document.getElementByID('D').innerText = 'Keep It!'
document.getElementByID('E').outerText = 'real!'
  • outer속성은 선택한 요소에 마크업을 새로 쓴다.
<div id="A"><strong>Hi</strong></div>
<div id="B" class="new">Da-ran!</div>
<span id="C">dude</span>
<div id="D">Keep It!</div> real!

3) innerHTML vs innerText vs textContent

innerHTML

  • innerHTML: 무거운 HTML파서를 호출하기에, 텍스트 노드만 생성하는 경우, textContent 사용을 권장한다.
    • innerHTML: 문자열 내에서 발견된 HTML요소를 실제 DOM노드로 변환
    • textContent: 텍스트 노드로만 생성

innerText (비표준)

  • innerText
    • 문서 내 노드들의 레이아웃, 포지션을 재계산 후 다시 뿌려주는 Reflow 가 발생
    • innerText는 스타일을 알고 있기 때문에, 숨겨진 요소들의 텍스트는 반환하지 않음
    • innerText는 텍스트를 정규화해서 반환, 공백, 줄바꿈, 개행문자 포함X
    • HTMLElement에 정의되어 있다.

textContent (DOM 사양)

  • textContent
    • <script>, <style>을 비롯한 모든 요소의 내용을 가져옴
    • 문서 내에 있는 것을 마크업만 제거해서 그대로 반환하기 때문에 공백, 줄바꿈 개행문자도 포함
    • 모든 Node에 정의되어 있다.
<span>Hello
  <span style="display: none;">World</span>
</span>

innerText // Hello
textContent // Hello World

4) textContent vs value

  • innerText: 엘리먼트의 텍스트 노드
  • value: input, textarea 등의 value 값

Tab을 눌렀을 때 공백 추가하기

 codeDOM.addEventListener("keydown",(e) => {
    if(e.key === 'Tab') {
        e.preventDefault();
        codeDOM.value += '    ';
        // codeDOM.textContent += '    ';    ❌
    }
});

5) document.write()

  • write()으로 전달된 값은 페이지가 로딩 및 해석되는 동안 페이지에 출력되기 때문에 로딩된 HTML문서가 해석 되는것을 지연/차단시키게 된다.

6) .insertAdjacentHTML()

  • Element 노드에서만 작동
  • 시작 태그의 앞, 뒤, 종료 태그의 앞, 뒤 태그에 노드를 삽입할 수 있다.
<i id="elm"></i>
var elm = document.getElementById('elm')

dle.insertAdjacentHTML('beforebegin','<div>')
dle.insertAdjacentHTML('afterbegin','<span>dude-</span>')
dle.insertAdjacentHTML('beforeend','<span>-are</span>')
dle.insertAdjacentHTML('afterend','</div>')
<div><span>dude-</span><i id="elm"></i><span>-are</span></div>
  • beforebegin, afterend 옵션은 노드가 DOM 트리 내에 존재하고 부모 요소를 가 가진 경우에만 동작한다.

9. DOM 트리를 Javascript 문자열로 추출하기

  • innerHTML, outerHTML, textContent
  • DOM에 노드를 생성하고 추가하는 데 사용하는 속성들이 Javascript 문자열로도 추출할 수 있다.
console.log(document.getElementByID('A').innerHTML);
// <i>Hi</i>

console.log(document.getElementByID('A').innerText);
// Dude!

console.log(document.getElementByID('A').textContent);
// Dude!

10. 노드를 DOM에 추가하기

  • appendChild(): 호출된 노드의 자식 노드 끝에 삽입
  • insertBefore(넣을 것, 어디다): 삽입 위치를 조정할 때 사용
    • 어디다 인자를 생략하면 appendChild()처럼 작동
<body>
  <p>Hi</p>
</body>
var elementNode = document.createElement('strong');
var textNode = document.createTextNode(' Dude');

document.querySelector('p').appendChild(elementNode)
document.querySelector('strong').appendChild(textNode)

console.log(document.body.innerHTML)
<p>Hi<strong> Dude</strong></p>
<ul>
  <li>2</li>
  <li>3</li>
</ul>
var text1 = document.createTextNode('1')
var li = document.createElement('li')
li.appendChild(text1)

var ul = document.querySelector('ul')
ul.insertBefore(li,ul,firstChild)

//  <ul>
//    <li>1</li>
//    <li>2</li>
//    <li>3</li>
//  </ul>

11. 노드 제거하기 / 바꾸기

  • removeChild(), replaceChild()
    • 해당 노드를 반환
    • parentNode 속성을 이용하여 부모 노드에 대한 접근을 얻어야함
divA.parentNode.removeChild(divA)
divA.parentNode.replaceChild(newSpan,divA)
  • 해당 노드를 삭제하는 것이 아니라, 해당 돔트리와의 연결을 해제시켜 문서의 범위를 벗어나게 만들기만 한다. 따라서 해당 노드에 대한 메모리상의 참조는 여전히 가지게 된다.
  • innerHTML, outerHTML, textContent 속성에 빈 문자열을 주는 쉽고 빠른 방법도 있지만, 실제로 노드를 메모리상에서 삭제하는 것이 아니기 때문에 브라우저 메모리 누수가 발생할 수 있다.

12. 노드 복제하기

  • Node.cloneNode(): 단일 노드 복제
  • Node.cloneNode(true): 하위 자식 노드까지 모두 복제
    • Element 반환
<ul>
  <li>Hi</li>
  <li>There!</li>
</ul>
var cloneUl = document.querySelector('ul').cloneNode();
cloneUl.innerHTML // ul만 복제되었기 때문에 빈 문자열이 출력
cloneUl // <ul></ul>

var cloneUl = document.querySelector('ul').cloneNode(true);
cloneUl.innerHTML // <li>Hi</li><li>There!</li>
cloneUl //
  • 노드 복제 시, 모든 특성 및 값도 복제된다.
  • addEventListener, node.onclick으로 추가된 것은 복제되지 않는다.

16. DOM 내의 노드 탐색하기

  • 현재 노드와 관련있는 다른 노드에 대한 정보를 얻을 수 있다.
    • parentNode
    • firstChild
    • lastChild
    • nextSibling
    • previousSibling
<body>
  <ul><!-- comment -->
  <li id="A"></li>
  <li id="B"></li>
  <!-- comment -->
  </ul>
</body>
var ul = document.querySelector("ul")

ul.parentNode.nodeName // body
ul.firstChild.nodeName // comment
ul.lastChild.nodeName // text (줄바꿈)

ul.querySelector('#A').nextSibling.nodeName
ul.querySelector('#B').previousSibling.nodeName 
// text

text와 comment 노드를 무시하고 DOM 탐색하기

  • firstElementChild
  • lastElementChild
  • nextElementSibling
  • previousElementSibling
  • children
  • parentElement
var ul = document.querySelector("ul")

ul.firstElementChild.nodeName
ul.lastElementChild.nodeName
// li

ul.querySelector('#A').nextElementSibling.nodeName 
ul.querySelector('#B').previousElementSibling.nodeName
// li


ul.children
// HTMLCollection
// 자식 노드는 text노드를 가짐

ul.firstElementChild.parentElement
// uㅣ

17. DOM트리 내의 Node위치 확인하기

  • Node.contains(): 특정 노드가 다른 노드를 포함하고 있는지 확인
document.querySelector('html').contains(document.querySelector('body'))
// true

//<html> 안에 <body>가 있다
  • compareDocumentPosition(): DOM 트리 내에서 주변 노드와 연관된 노드 위치가 담킨 코드를 반환한다.
반환코드의미
0동일 Element
1DOCUMENT_POSITION_DISCONNECTED
선택된 노드와 전달된 노드가 동일한 문서에 존재하지 않음.
2DOCUMENT_POSITION_PRECEDING
전달된 노드가 선택된 노드 앞에 있음.
4DOCUMENT_POSITION_FOLLOWING
전달된 노드가 선택된 노드 뒤에 있음.
8DOCUMENT_POSITION_CONTAINS
전달된 노드가 선택된 노드의 조상임.
16,10DOCUMENT_POSITION_CONTAINED_BY
전달된 노드가 선택된 노드의 자손임.
  • 숫자코드는 특정 노드가 다른 노드와 하나 이상의 관계를 가질 수 있다.
  • 노드가 포함 관계(16)이자, 앞에 있는 경우(4)는 16+4=20을 반환한다.

18. 두 노드가 동일한지 판단하기

  • Node.isEqualNode(): 매개변수로 전달하는 노드와 동일한지 확인

두 노드의 동일 조건

  1. nodeName, localName, namespaceURI, prefix, nodeValue 문자열 특성이 동일해야한다.
  2. NameNodeMaps 특성이 동일하다.
    • 둘 다 null이거나 길이가 동일해야 하며, 하나의 맵 내에 존재하는 각 노드들과 다른 맵에 존재하는 노드가 동일해야 하되 인덱스가 동일할 필요는 없다.
  3. childNodes NodeLists가 동일해야한다.
    • 정규화가 동일성에 영향을 미칠 수 있으므로 비교 수행 전에 노드를 정규화해야한다.
<input type="text">
<input type="text">

<textarea>foo</textarea>
<textarea>bar</textarea>
var input = document.querySelectorAll('input');
input[0].isEqualNode(input[1]) //true

var textarea = document.querySelectorAll('textarea');
textarea[0].isEqualNode(textarea[1]) // false
  • 두 노드가 동일한 노드를 참조하고 있는지 알고 싶다면 === 연산자를 사용한다.