DOM

beomjin_97·2022년 12월 12일
0

javascript

목록 보기
13/13

1. 노드

HTML요소는 렌더링 엔진에 의해 파싱되어 dom을 구성하는 노드 객체로 변환된다.

  • 문서 노드 : dom tree의 최상위 루트 노드, document또는 window.document로 참조, HTML문서당 1개의 document객체, 노드들의 entry point.
  • 요소 노드 : HTML 요소를 가리키는 객체.
  • 어트리뷰트 노드 : 요소의 어트리뷰트를 가리키는 객체, 요소 노드에만 연결됨
  • 텍스트 노드 : 텍스트를 가리키는 객체, 요소 노드의 자식 노드이자 리프 노드, dom tree의 최종단

모든 노드 객체는 Object - EventTarget - Node 인터페이스를 상속받는다.





2. 요소노드 취득

2.1 Document.prototype.getElementById( )

const $elem = document.getElementById('id')
여러개의 동일한 아이디를 가진 요소가 있다면 첫번째 요소 노드만 반환한다.

2.2 Document.prototype/Element.prototype.getElementsByTagName( )

const $elem = document.getElementsByTagName('li')
인수로 전달한 태그 이름을 갖는 모든 요소 노드들을 탐색하여 HTMLCollection객체로 반환한다.
HTMLCollection은 유사배열객체이자 이터러블이다.
Element.prototype에 의해 정의된 메서드는 특정 요소 노드의 자손 노드 중에서 요소 노드를 탐색하여 반환한다.
const $liElem = $elem.getElementsByTagName('li')

2.3 Document.prototype/Element.prototype.getElementsByClassName( )

const $elem = document.getElementsByClassName('class')
인수로 전달한 class 값을 갖는 모든 요소 노드들을 탐색하여 HTMLCollection객체로 반환한다.
HTMLCollection은 유사배열객체이자 이터러블이다.
공백으로 구분하여 여러개의 클래스를 지정할 수도 있고 특정 요소 노드의 자손 노드 중에서 요소 노드를 탐색할 수도 있다.
const $liElem = $elem.getElementsByClassName('class1 class2')

2.5 Document.prototype/Element.prototype.querySelector( )

인수로 전달한 css 선택자를 만족시키는 하나의 요소노드를 탐색하여 반환한다.
const $elem = document.querySelector('ul > li')
특정 요소 노드의 자손 노드 중에서 요소 노드를 탐색할 수도 있다.

2.6 Document.prototype/Element.prototype.querySelectorAll( )

인수로 전달한 css 선택자를 만족시키는 모든 요소를 탐색하여 Nodelist 객체를 반환한다.
NodeList객체는 유사 배열 객체 이면서 이터러블이다.
const $elem = document.querySelectorAll('ul > li')

2.7 Element.prototype.match( )

인수로 전달한 css 선택자를 통해 특정 요소를 취할 수 있는지 확인한다.

const $apple = document.querySelector('.apple');
$apple.matches('$fruits > li.apple') // true

2.8 HTMLCollection, NodeList

코드가 동작하는 동안 실시간으로 변할 수도 있는 객체이기 때문에 배열로 변환하여 사용하는 것이 바람직하다.





3. 노드 탐색

3.1 공백 텍스트 노드

HTML 요소 사이 스페이스, 탭, 개행 등의 공백문자는 텍스트 노드를 생성한다.
노드 탐색시 주의해야 한다.

3.2 자식 노드 탐색

3.2.1 Node.prototype.childNodes

자식 노드 모두를 Nodelist에 담아 반환한다. 요소노드 뿐만 아니라 텍스트 노드도 포함될 수 있다.

3.2.2 Element.prototype.children

요소노드인 자식노드만 탐색하여 HTMLCollection에 담아 반환한다.

3.2.3 Node.prototype.firstChild

텍스트 노드 또는 요소 노드 모두 포함해서 첫번째 자식 요소를 반환한다.

3.2.4 Node.prototype.lastChild

텍스트 노드 또는 요소 노드 모두 포함해서 마지막 자식 요소를 반환한다.

3.2.5 Element.prototype.firstElementChild

첫번째 자식 요소 노드를 반환한다.

3.2.6 Element.prototype.lastElementChild

마지막 자식 요소 노드를 반환한다.

3.2.7 Node.prototype.hasChildNodes

텍스트 노드와 요소 노드를 포함해서 자식 노드가 존재하면 true, 존재하지 않으면 false를 반환한다. 요소 노드만 확인하고 싶을 때는 children.length로 확인해보자.


3.3 부모 노드 탐색

3.3.1 Node.prototype.parentNode

요소의 부모노드를 반환한다.


3.4 형제 노드 탐색

어트리뷰트 노드는 요소 노드와 연결되어 있지만 부모 노드가 같은 형제 노드가 아니기 때문에 텍스트 노드 또는 형제 노드만 반환된다.

3.4.1 Node.prototype.previousSibling

요소 노드와 텍스트 노드를 포함해서 형제 노드중 이전 형제 노드를 반환한다.

3.4.2 Node.prototype.nextSibling

요소 노드와 텍스트 노드를 포함해서 형제 노드중 다음 형제 노드를 반환한다.

3.4.3 Element.prototype.previousElementSibling

형제 노드 중 이전 요소 형제 노드를 반환한다.

3.4.4 Element.prototype.nextElementSibling

형제 노드 중 다음 요소 형제 노드를 반환한다.





4. 노드 정보 취득

4.1 Node.prototype.nodeType

노드 정보를 나타내는 프로퍼티로 노드 타입을 나타내는 상수를 반환

  • 요소 노드는 1을 반환
  • 텍스트 노드는 3을 반환
  • 문서 노드는 9를 반환

4.2 Node.prototype.nodeName

노드를 이름을 문자열로 반환

  • 요소노드는 대문자 문자열('LI', 'UL')을 반환
  • 텍스트노드는 '#text'를 반환
  • 문서노드는 '#document'를 반환



5. 요소 노드의 텍스트 조작

5.1 Node.prototype.nodeValue

텍스트 노드의 텍스트 값을 참조하거나 할당하는 프로퍼티이다. 텍스트노드가 아닌 노드에서 참조하면 null을 반환한다.

const $textNode = document.getElementById('foo').firstChild;
$textNode.nodeValue = 'hello';
console.log($text.nodeValue); // hello

5.2 Node.prototype.textContent

참조하게 되면 요소 노드의 시작 태그와 종료 태그 사이의 모든 텍스트를 반환한다.
문자열을 할당하면 요소 노드의 모든 자식 노드가 제거 되고 할당한 문자열이 텍스트로 추가된다.
참조시와 할당시 모두 HTML 마크업은 무시된다.

<body>
  <div id="foo">
    Hello
    <span>World!</span>
  </div>
</body>
<script>
  let $foo = document.getElementById('foo');	
  console.log($foo.textContent) // Hello World
  
  $foo.textContent = 'Hi <span>there!<span>'  // HTML 마크업이 파싱되지 않는다.
</script>



6. Dom 조작

Dom manipulation : 새로운 노드를 생성하여 추가, 또는 기존 노드 삭제, 교체 하는것
리플로우와 리페인트가 발생하므로 성능 최적화를 위해 주의해서 다룰 것.

6.1 innerHTML

setter와 getter 모두 존재
참조시 요소 노드의 시작 태그와 종료 태그 사이의 모든 HTML 마크업을 문자열로 반환한다.
문자열을 할당하면 모든 자식요소를 제거하고 할당한 문자열에 포함되어 있는 HTML 마크업이 파싱되어 자식노드로 DOM에 반영된다.
XSS 위험이 존재한다. (DOMPurify 라이브러리 사용 권장)

<body>
  <div id="foo">
    Hello
    <span>World!</span>
  </div>
</body>
<script>
  let $foo = document.getElementById('foo');	
  console.log($foo.innerHTML) // 'Hello<span>World!</span>'
  
  $foo.innerHTML = 'Hi <span>there!<span>'  // HTML 마크업이 파싱된다.
</script>

6.2 insertAdjacentHTML

기존 요소를 제거하지 않으면서 위치를 지정해 새로운 요소를 삽입한다.
첫번째 인수: 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend'
두번째 인수: 문자열에 포함되어 있는 HTML 마크업이 파싱되어 자식노드로 DOM에 반영

<!DOCTYPE html>
<html>
  <body>
    <!-- beforebegin -->
    <div id="foo">
      <!-- afterbegin -->
      text
      <!-- beforeend -->
    </div>
    <!-- afterend -->
  </body>
  <script>
    const $foo = document.getElementById("foo");

    $foo.insertAdjacentHTML('beforebegin', '<p>beforebegin</p>')
    $foo.insertAdjacentHTML('afterbegin', '<p>afterbegin</p>')
    $foo.insertAdjacentHTML('beforeend', '<p>beforeend</p>')
    $foo.insertAdjacentHTML('afterend', '<p>afterend</p>')
  </script>
</html>

6.3 노드 생성과 추가

6.3.1 Document.prototype.createElement( )

태그 이름을 나타내는 문자열을 인수로 전달하여 요소 노드 생성 후 반환
기존 DOM과는 별개로 존재한다.

const liEl = document.createElement('li')

6.3.2 Document.prototype.createTextNode( )

문자열을 전달하여 텍스트 노드를 생성 후 반환
요소 노드와 별개로 존재한다.

const textNode = document.createTextNode('hello')

6.3.3 Document.prototype.appendChild( )

인수로 들어온 노드를 메서드를 호출한 노드의 마지막 자식노드로 추가한다.

const ulEl = ducument.querySelector('ul')
const liEl = document.createElement('li')
const textNode = document.createTextNode('hello')

liEl.appendChild(textNode)
ulEl.appendChild(liEl)

6.4 복수의 노드 생성과 추가

여러개의 노드를 생성하여 생성될 때 마다 각각 추가하게 되면 매 실행마다 리플로우와 리페인트가 발생하므로 성능 최적화에 불리하다.
이때는 컨테이너를 사용하여 컨테이너에 생성하는 요소들을 담은 후 컨데이터를 DOM에 추가하면 DOM은 단 한 번만 변경된다.

6.4.1 Document.prototype.createDocumentFragment

documentFragment 노드는 컨테이너처럼 자식 노드들을 담아두는데 사용한다.
DOM과 별도로 존재하다가 DOM에 추가되면 자신은 제거되고 자식노드만 DOM에 추가된다.

const $fruit = document.getElementById('fruit')
    const $fragment = document.createDocumentFragment();

    ['apple', 'banana', 'orange'].forEach(text => {
      const $li = document.createElement('li')
      const textNode = document.createTextNode(text)
      $li.appendChild(textNode)
      $fragment.appendChild($li)
    })
    
    fruit.appendChild($fragment)

6.5 노드 삽입

6.5.1 Node.prototype.insertBefore( )

첫 번째 인수로 전달받은 노드를 두 번째 인수로 전달받은 노드 앞에 삽입한다.
두 번째 인수로 전달 받은 노드는 반드시 메서드를 호출한 노드의 자식 노드이어야 한다.

const $fruit = document.getElementById('fruit')
const $li = document.createElement('li')
const textNode = document.createTextNode('orange')
    
$fruit.insertBefore($li, $fruit.lastElementChild)

6.6 노드 이동

DOM에 이미 존재하는 노드를 appendChild 또는 insertBefore 메서드를 사용하여 DOM에 다시 추가하면 현재 위치에서 해당 노드를 제거하고 새로운 위치에 노드를 추가한다.

6.7 노드 복사

6.7.1 Node.prototype.cloneNode( )

노드의 사본을 생성하여 반환한다. true를 인수로 전달하면 deep clone하여 모든 자손 노드를 포함하여 생성한다. 생락하여나 false를 전달하면 텍스트 노드도 포함하지 않은 노드 자신만의 사본을 생성한다.

const $shallowClone = $apple.cloneNode()
const $deepClone = $apple.coneNode(true)

6.8 노드 교체

6.8.1 Node.prototype.replaceChild( )

교체할 새로운 노드를 첫 번째 인수로 전달하고 이미 존재하는 교체될 노드를 두 번째 인수로 전달한다. 두번 째 인수로 전달 된 노드는 메서드를 호출한 노드의 자식 노드이어야 한다.

6.9 노드 삭제

6.9.1 Node.prototype.removeChild( )

매개변수에 인수로 전달한 노드를 DOM에서 삭제 한다. 인수로 전달한 노드는 메서드를 호출한 노드의 자식 노드이어야 한다.




7. 어트리뷰트

7.1 어트리뷰트 노드와 attributes 프로퍼티

HTML 문서가 파싱될 때 HTML attributes는 어트리뷰트 노드로 변환되어 요소 노드와 연결된다.
모든 어트리뷰트 노드는 NamedNodeMap 객체에 담겨 요소 노드의 attributes 프로퍼티에 저장된다.
Element.prototype.attributes로 참조(만) 가능하다.

7.2 어트리뷰트 조작

7.2.1 Element.prototype.getAttribute( )

요소 노드에서 어트리뷰트 값을 참조할 수 있다.

const $input = document.querySelector('foo')
const inputValue = $input.getAttribute('value')

7.2.2 Element.prototype.setAttribute( )

요소 노드에서 어트리뷰트 값을 변경할 수 있다.

const $input = document.querySelector('foo')
$input.setAttribute('value', 'bar')

7.2.3 Element.prototype.hasAttribute( )

특정 어트리뷰트가 존재하는지 boolean 으로 반환한다.

const $input = document.querySelector('foo')
const hasValue = $input.hasAttributes('value')

7.2.4 Element.prototype.removeAttribute( )

특성 어트리뷰트를 삭제할 수 있다.

const $input = document.querySelector('foo')
$input.removeAttributes('value')

7.3 HTML 어트리뷰트 vs DOM 프로퍼티

요소노드의 초기 상태는 어트리뷰트 노드가 관리하고 요소 노드의 최신 상태는 DOM 프로퍼티가 관리한다.
어트리뷰트 노드의 값은 사용자의 입력에도 변하지 않고 DOM 프로퍼티는 사용자의 입력을 받아낸다.

const $input = document.getElementById('user')
const attr = $input.getAttribute('value')  // 초기 상태
const property = $input.value; // 최신 상태
  • id 어트리뷰트는 id 프로퍼티와 동일한 값으로 대응
  • class 어트리뷰트는 className, classList와 대응
  • for 어트리뷰트는 htmlFor와 대응

7.4 data 어트리뷰트와 dataset프로퍼티

data 어트리뷰트와 dataset 프로퍼티로 데이터를 교환할 수 있다.

<!DOCTYPE html>
<html>
  <body>
    <ul>
      <li id="1" data-user-id="7555" data-role="admin">Lee</li>
      <li id="1" data-user-id="4581" data-role="subscriber">Kim</li>
    </ul>
  </body>
  <script>
    const users = [...document.querySelector('.users').children]
    const user = users.find((user) => user.dataset.userId === '7555')
    console.log(user.dataset.role) // 'admin'
    
    user.dataset.role = 'subscriber'
    console.log(user.dataset)  // DOMSTRingMap {userId: '7555', role: 'subscriber'}
  </script>
</html>



8. 스타일

8.1 인라인 스타일 조작

8.2 클래스 조작

8.2 스타일 참조

profile
Rather be dead than cool.

0개의 댓글