공부공부/JS 딥다이브

[js 딥다이브] 39장 DOM

고생쨩 2024. 2. 18. 10:01
728x90

DOM

노드

HTML요소
html문서를 구성하는 개별적인 요소.
html 문서는 요소의 집합이며, 중첩 관계를 갖는다.

트리 자료 구조
부모와 자식으로 구성되며 최상위 노드는 root 노드라 함.
루트는 0개이상의 자식을 갖는다.
자식이 없는 노드는 리프 노드라 함.
노드 객체들로 구성된 트리 자료구조를 DOM 혹은 DOM 트리라고 함.

노드 객체의 타입

  1. 문서노드 - 최상위 객체로 document 객체를 가르킴.
  2. 요소노드 - html 요소를 가르킴.
  3. 어트리뷰트 노드 - html요소의 어트리뷰트를 가르킴. 부모와 상관없이 요소노드에만 연결되어 있음.
  4. 텍스트 노드 - html요소의 text를 가르킴. 자식 노드를 가질 수 없음.

노드 객체의 상속 구조

Object
└── EventTarget
    └── Node
        ├── Document
        │     └── HTMLDocumnet
        ├── Element
        │     ├── HTMLElement
        │     │     ├── HTMLHtmlElement
        │     │     ├── HTMLHeadElement
        │     │     ├── HTMLMetaElement
        │     │     ├── HTMLLinkElement
        │     │     ├── HTMLScriptElement
        │     │     ├── HTMLBodyElement
        │     │     ├── HTMLUListElemnt
        │     │     ...
        ├── Attr
        └── CharacterData
              ├── Text
              └── Comment

요소 노드 취득

/* 
  id를 이용한 요소 노드 취득
  : 해당 id를 갖는 첫번째 요소 노드만 반환. 없을 경우 null
*/
document.getElementById('id');

/* 
  태그이름을 이용한 요소 노드 취득
  : 해당 태그의 모든 요소를 반환(유사배열이자 이터러블)
  : 없을 경우 빈 HTMLCollection 객체를 반환
*/
document.getElementsByTagName('tag');

/* 
  class를 이용한 요소 노드 취득
  : 해당 class를 갖는 모든 노드만 반환.
  : 없을 경우 빈 HTMLCollection 객체를 반환
*/
document.getElementsByClassname('name');

/* 
  class 선택자를 이용한 요소 노드 취득
  : 없을 경우 null
  : 선택자가 문법에 안 맞으면 DOMException 에러
*/
document.querySelector('selector'); //조건에 맞는 첫번째 노드
document.querySelectorAll('slectror'); //조건에 맞는 모든 노드

/* 
  특정 요소 노드를 취득할 수 있는지 확인
*/
 <ul id="fruits">
  <li class="apple">Apple</li>
  <li class="banana">Banana</li>
  <li class="orange">Orange</li>
 </ul>
 
 <script>
  const $apple = document.querySelector('.apple');
  // $apple 노드는 '#fruits > li.apple'로 취득할 수 있다.
  console.log($apple.matches('#fruits > li.apple')); // true
  // $apple 노드는 '#fruits > li.banana'로 취득할 수 없다.
  console.log($apple.matches('#fruits > li.banana')); // false
 </script>

HTMLCollection과 NodeList
DOM API가 여러개의 결과값을 반환하기 위한 DOM 컬렉션 객체.
유사배열이면서 이터러블.
노드 객체의 상태 변화를 실시간으로 반영하는 Live 객체. (HTMLCollection은 항상 live, NodeList는 경우에 따라 live)

예상치 못한 동작을 할 수 있으므로 배열로 변환하여 사용한다.

 const $fruits = document.getElementById('fruits');
 // childNodes 프로퍼티는 NodeList 객체(live)를 반환한다.
 const { childNodes } = $fruits;
 // 스프레드 문법을 사용하여 NodeList 객체를 배열로 변환한다.
 [...childNodes].forEach(childNode => {
 $fruits.removeChild(childNode);
 });
 // $fruits 요소의 모든 자식 노드가 모두 삭제되었다.
 console.log(childNodes); // NodeList []

노드 탐색

parentNode, previousSibling, firstChild, childNodes 프로퍼티는 Node.prototype이 제공하고, 프로퍼티
키에 Element가 포함된 previousElementSibling, nextElementSibling과 children 프로퍼티는 Element.
prototype이 제공한다.

공백 텍스트 노드 공백 문자는 공백 텍스트 노드를 생성함.
자식 노드 접근

프로퍼티 설명
Node.prototype.childNodes 자식 노드를 모두 탐색하여 DOM 컬렉션 객체인 NodeList에 담아 반환한다. childNodes 프로퍼티가 반환한 NodeList에는 요소 노드뿐만 아니라 텍스트 노드도 포함되어 있을 수 있다.
Element.prototype.children 자식 노드 중에서 요소 노드만 모두 탐색하여 DOM 컬렉션 객체인 HTMLCollection에 담아 반환한다. children 프로퍼티가 반환한 HTMLCollection에는 텍스트 노드가 포함되지 않는다.
Node.prototype.firstChild 첫 번째 자식 노드를 반환한다. firstChild 프로퍼티가 반환한 노드는 텍스트 노드이거나 요소 노드다.(텍스트 노드에 접근할때 일반적으로 사용)
Node.prototype.lastChild 마지막 자식 노드를 반환한다. lastChild 프로퍼티가 반환한 노드는 텍스트 노드이거나 요소 노드다.
Element.prototype.firstElementChild 첫 번째 자식 요소 노드를 반환한다. firstElementChild 프로퍼티는 요소 노드만 반환한다.
Element.prototype.lastElementChild 마지막 자식 요소 노드를 반환한다. lastElementChild 프로퍼티는 요소 노드만 반환한다.
Node.prototype.hasChildNodes 자식 노드가 존재하면 true, 없으면 false

부모 노드 접근

프로퍼티 설명
Node.prototype.parentNode 부모 노드를 탐색

형제 노드 접근

프로퍼티 설명
Node.prototype.previousSibling 부모 노드가 같은 형제 노드 중에서 자신의 이전 형제 노드를 탐색하여 반환한다. previousSibling 프로퍼티가 반환하는 형제 노드는 요소 노드뿐만 아니라 텍스트 노드일 수도 있다.
Node.prototype.nextSibling 부모 노드가 같은 형제 노드 중에서 자신의 다음 형제 노드를 탐색하여 반환한다. nextSibling 프로퍼티가 반환하는 형제 노드는 요소 노드뿐만 아니라 텍스트 노드일 수도 있다.
Element.prototype.previousElementSibling 부모 노드가 같은 형제 요소 노드 중에서 자신의 이전 형제 요소 노드를 탐색하여 반환한다. previousElementSibling 프로퍼티는 요소 노드만 반환한다.
Element.prototype.nextElementSibling 부모 노드가 같은 형제 요소 노드 중에서 자신의 다음 형제 요소 노드를 탐색하여 반환한다. nextElementSibling 프로퍼티는 요소 노드만 반환한다

노드 정보 취득

프로퍼티 설명
Node.prototype.nodeType 노드 객체의 종류, 즉 노드 타입을 나타내는 상수를 반환한다. 노드 타입 상수는 Node에 정의되어 있다.
■ Node.ELEMENT_NODE: 요소 노드 타입을 나타내는 상수 1을 반환
■ Node.TEXT_NODE: 텍스트 노드 타입을 나타내는 상수 3을 반환
■ Node.DOCUMENT_NODE: 문서 노드 타입을 나타내는 상수 9를 반환
Node.prototype.nodeName 노드의 이름을 문자열로 반환한다.
■ 요소 노드: 대문자 문자열로 태그 이름(“UL”, “LI” 등)을 반환
■ 텍스트 노드: 문자열 "#text"를 반환
■ 문서 노드: 문자열 "#document"를 반환

요소 노드의 텍스트 조작

nodeValue
참조와 할당 모두 가능. 텍스트 노드의 nodeValue를 참조할때만 텍스트를 반환하고 다른 노드는 null 반환.

textContent
요소 노드의 텍스트와 모든 자손 노드의 텍스트를 모두 취득하거나 변경(태그는 무시)

DOM 조작

새로운 노드가 추가되거나 삭제되면 리플로우와 리페인트가 발생.

innerHTML
html마크업을 취득하거나 변경
사용시 자식 노드를 모두 제거하고 변경한다.
XSS에 취약하므로 사용자 데이터를 그대로 쓰는 경우는 없어야함.
XSS 방지책으로 책에서는 DOMPurify를 추천하였음.
대안으로 Sanitizer API가 표준화 과정중에 있음.
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API
또다른 방법으로는 정규식을 이용한 화이트 리스트나 블랙 리스트 방법이 있음.

insertAdjacentHTML 메서드
기존요소를 제거하지 않으면서 위치를 지정해 새로운 요소를 삽입.
위치 - ‘beforebegin’, ‘afterbegin’, ‘beforeend’, ‘afterend’
XSS에 취약한 건 동일.

노드 생성과 추가

  1. createElement : 요소노드 생성
  2. createTextNode : 텍스트 노드 생성
  3. appendChild : 요소노드를 DOM에 추가(이때 리플로우와 리페인트가 실행됨)

복수의 노드 생성과 추가
Document.prototype.createDocumentFragment를 이용하여 묶어서 처리한다.

노드 삽입

  1. appendChild : 마지막 자식 노드로 추가
  2. insertBefore : 두번째 인수로 전달받은 노드 앞에 삽입

노드 이동

<ul id="fruits">
  <li>Apple</li>
  <li>Banana</li>
  <li>Orange</li>
</ul>

<script>
  const $fruits = document.getElementById('fruits');
  // 이미 존재하는 요소 노드를 취득
  const [$apple, $banana, ] = $fruits.children;
  // 이미 존재하는 $apple 요소 노드를 #fruits 요소 노드의 마지막 노드로 이동
  $fruits.appendChild($apple); // Banana - Orange - Apple
  // 이미 존재하는 $banana 요소 노드를 #fruits 요소의 마지막 자식 노드 앞으로 이동
  $fruits.insertBefore($banana, $fruits.lastElementChild);
  // Orange - Banana - Apple
</script>

노드 복사
Node.prototype.cloneNode([deep: true | false]) 메서드는 노드의 사본을 생성하여 반환한다. 매개변수 deep에 true를 인수로 전달하면 노드를 깊은 복사deep copy하여 모든 자손 노드가 포함된 사본을 생성하고,
false를 인수로 전달하거나 생략하면 노드를 얕은 복사shallow copy하여 노드 자신만의 사본을 생성.

노드 교체
Node.prototype.replaceChild(newChild, oldChild) 메서드는 자신을 호출한 노드의 자식 노드를 다른 노드로 교체한다. 첫 번째 매개변수 newChild에는 교체할 새로운 노드를 인수로 전달하고, 두 번째 매개변수
oldChild에는 이미 존재하는 교체될 노드를 인수로 전달한다. oldChild 매개변수에 인수로 전달한 노드는 replaceChild 메서드를 호출한 노드의 자식 노드이어야 한다.

노드 삭제
Node.prototype.removeChild(child) 메서드는 child 매개변수에 인수로 전달한 노드를 DOM에서 삭제한다.

어트리뷰트

.getAttribute('key');
.setAttribute('key','value');
.value // .키값
<tag data-name="abc" data-age="17">
.dataset.name
.dataset.age

스타일

.style
.style.width
.style['width']
//px, em, %등 단위 지정 필수

.className //문자열 반환
.classList //DOMTokenList 반환

.add
.remove
.item //인수의 인덱스
.contains //포함여부
.replace //변환
.toggle //있으면 제거, 없으면 추가

window.getComputedStyle(el)
style 프로퍼티는 인라인 스타일만 반환. class까지 포함하여 반환할때 사용. 두번째 인자로 :before, :after 사용

window.getComputedStyle($box, ':before');
window.getComputedStyle($box, ':after');

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.