딥다이브 : DOM

jonyChoiGenius·2023년 10월 2일
0
post-thumbnail

DOM은 자바 스크립트를 통해 HTML 문서와 그 요소에 접근하고 조작할 수 있는 API를 제공한다.
DOM은 트리 형태의 자료구조이며, HTML 요소를 지시하는 ‘요소 노드’가 그 계층 관계를 표현한다.

DOM은 HTML 문서의 계층적 구조와 정보를 표현하며,

자바스크립트로 이를 제어할 수 있는 API (프로퍼티와 메서드)를 제공한다.

자바 스크립트 HTML DOM (w3schools.com)

개체 모델을 사용하면 JavaScript가 만드는 데 필요한 모든 기능을 얻을 수 있습니다 동적 HTML:

  • 자바 스크립트는 페이지의 모든 HTML 요소를 변경할 수 있습니다
  • 자바 스크립트는 페이지의 모든 HTML 속성을 변경할 수 있습니다.
  • 자바 스크립트는 페이지의 모든 CSS 스타일을 변경할 수 있습니다.
  • 자바 스크립트는 기존 HTML 요소 및 특성을 제거할 수 있습니다.
  • 자바 스크립트는 새로운 HTML 요소 및 속성을 추가할 수 있습니다
  • 자바 스크립트는 페이지의 모든 기존 HTML 이벤트에 반응할 수 있습니다
  • 자바 스크립트는 페이지에 새 HTML 이벤트를 만들 수 있습니다.

39.1 노드

39.1.1 HTML 요소와 노드 객체

HTML 요소는 시작 태그, 어트리뷰트, 콘텐츠, 끝 태그를 포함한다.

노드 객체는 DOM을 구성하는 객체이며, 12개의 종류가 있다.
DOM 트리의 루트 노드인 ‘문서 노드’
HTML 요소를 지시하는 ‘요소 노드’,
HTML 요소의 어트리뷰트를 지시하는 ‘어트리뷰트 노드’
공백과 텍스트 콘텐츠를 지시하는 ‘텍스트 노드’,
주석을 지시하는 ‘주석 노드’ 등이 있다.

HTML 요소

HTML 문서를 구성하는 개별 요소. 시작 태그와 종료 태그, 콘텐츠를 포함한다.

노드 객체

DOM을 구성하는 요소. 12개의 종류가 있다.

DOM 트리

DOM은 Document 노드를 Root로 하는 노드 객체들의 트리 자료구조이다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <ul>
    <li id="apple">Apple</li>
    <li id="banana">Banana</li>
    <li id="orange">Orange</li>
  </ul>
  <script src="app.js"></script>
</body>
</html>

39.1.2 노드 객체의 타입

문서 노드

  • document 객체를 지시
  • DOM트리의 루트 노드

요소 노드

  • HTML 요소를 지시
  • 요소 노드 간 부자 관계를 가지며, 문서의 구조를 표현

어트리뷰트 노드

  • HTML 요소의 어트리뷰트를 지시
  • 요소 노드와 연결됨. (요소 노드의 형제 노드는 아님. - 부모가 같지 않으므로)

텍스트 노드

  • HTML 요소의 텍스트를 가리키는 객체

https://www.w3schools.com/jsref/prop_node_nodetype.asp

TypeDescriptionChildren
1Element요소 노드를 가리킴Element, Text, Comment, ProcessingInstruction, CDATASection, EntityReference
2Attr어트리뷰트 노드를 가리킴Text, EntityReference
3Text요소 혹은 어트리뷰트의 텍스트 콘텐츠를 가리킴None
4CDATASectionCharacter DATA를 가리킴 (text that will NOT be parsed by a parser)None
5EntityReferenceRepresents an entity referenceElement, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
6EntityRepresents an entityElement, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
7ProcessingInstructionRepresents a processing instructionNone
8Comment주석을 가리킴None
9Document전체 문서를 가리킴(돔 트리의 루트 노드)Element, ProcessingInstruction, Comment, DocumentType
10DocumentType문서의 종류를 정의함None
11DocumentFragment문서의 일부를 보관할 수 있는 “경량” 문서 객체Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
12NotationRepresents a notation declared in the DTDNone

39.1.3 노드 객체의 상속 구조

이벤트를 제어하는 EventTarget 객체, Node 객체, HTML 요소를 의미하는 Element 객체 등이 각각의 HTML 요소 객체에 상속된다.

특정 HTML 요소에서 메서드, 프로퍼티에 접근하더라도
Node 객체에서 제공하는 메서드는 주석 노드, 텍스트 노드 등을 함께 반환하도록 작동되며,
Element 객체에서 제공하는 메서드는 HTML 요소만을 반환하도록 작동된다.

노드 객체는 자바스크립트 객체이므로 프로토타입에 의한 상속 구조를 갖는다.

(단, 노드 객체는 ECMA스크립트 사양으로 제공되는 것(built-in objects)이 아닌, 브라우저 환경에서 제공되는 객체(host objects)이다.)

Node 객체는 Object, EventTarget 인터페이스를 상속받는다.

Node 인터페이스는 Document, Element, Attr, CharacterData로 상속된다.

결과적으로, HTMLLinkElement는 HTMLElement, Element, Node, EventTarget, Object 객체의 프로토타입의 인터페이스를 모두 사용할 수 있게 된다. HTML 요소의 다양한 특성과 DOM API는 이렇게 상속받아 생기는 것들이다.

모든 노드 객체는

EventTarget 객체를 상속받기 때문에 이벤트를 발생시킬 수 있다. (e.g. EventTarget.addEventListener)

Node 객체를 상속받기 때문에 부모, 자식, 형제 노드를 조회할 수 있다.(e.g. Node.parentNode)

39.2 요소 노드 취득(Accessing the DOM)

Id 값을 통해 하나의 노드를 취득할 때에는 document.getElementById 메서드를 주로 사용하며,
여러 개의 요소 노드를 취득할 때에는 document.querySelectorAll/ element.querySelectorAll 메서드를 사용하면 코딩 컨벤션을 통일할 수 있는 장점이 있다.

Document 객체와 Element 객체 모두 querySelector 메서드를 가지고 있으며, element.querySelectorAll 는 특정 요소의 자식 요소 중에서 탐색하여 반환한다.

39.2.1 id를 통한 취득

Document.prototype.getElementById("아이디")

  • id 어트리뷰트 값을 인수로 받아 하나의 노드를 반환한다.
  • 같은 id가 여러 개 있어도 첫 번째 요소 노드만 반환한다.
  • 같은 id가 없는 경우 null을 반환한다.
  • id 값과 동일한 이름의 전역 변수가 암묵적으로 선언된다.
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="foo"></div>
    <div id="bar"></div>
    <script>
      console.log(foo === document.getElementById("foo")) //true

      //명시적으로 변수를 선언한 경우에는 노드 객체가 할당되지 않는다.
      let bar = 1
      console.log(bar === document.getElementById("bar")) //false
    </script>
  </body>
</html>

39.2.2 태그 이름을 이용한 요소 노드 취득

Document.prototype.getElementsByTagName("태그명")

Element.prototype.getElementsByTagName("태그명")

  • 해당 태그명의 요소 노드를 모두 반환한다.
  • 반환되는 객체는 HTMLCollection이라는 이터러블한 유사 배열 객체이다.
<!DOCTYPE html>
<html lang="en">
  <body>
    <ul>
      <li id="apple">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
    </ul>
    <script>
      const $elems = document.getElementsByTagName("li")
			
			//유사 배열 객체를 배열로 변환하여 순회할 수 있다.
      Array($elems).forEach(e=> console.log(e))
      //HTMLCollection(3) [li#apple, li#banana, li#orange, apple: li#apple, banana: li#banana, orange: li#orange]
    </script>
  </body>
</html>
  • ‘*’ 을 인수로 넘겨 모든 노드를 반환받을 수 있다.
  • Element.prototype.getElementsByTagName 은 특정 요소의 자식 요소에서 탐색을 진행한다.
<!DOCTYPE html>
<html lang="en">
  <body>
    <ul>
      <li id="apple">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
    </ul>
    <ul id="cars">
      <li id="bmw">BMW</li>
      <li id="audi">Adui</li>
      <li id="mercedes">Mercedes</li>
    </ul>
    <script>
      const $elem = document.getElementById("cars")
      const $elems = $elem.getElementsByTagName("li")

			// 이터러블한 객체는 구조분해 할당을 통해 배열로 만들 수 있다.
      [...$elems].forEach(e=> console.log(e))
      //HTMLCollection(3) [li#bmw, li#audi, li#mercedes, bmw: li#bmw, audi: li#audi, mercedes: li#mercedes]
    </script>
  </body>
</html>

39.2.3 class를 이용한 요소 노드 취득

Document.prototype.getElementsByClassName("클래스명1 클래스명2")

Element.prototype.getElementsByClassName("클래스명1 클래스명2")

  • 클래스명으로 여러 개의 노드를 탐색하여 반환한다.
  • 공백을 이용하여 여러 개의 클래스를 지정할 수 있으며, 해당 클래스들을 모두 가진 요소를 반환한다.
  • Element.prototype.getElementsByClassName은 특정 요소의 자식 요소에서 탐색을 진행한다.

39.2.4 CSS 선택자를 이용한 요소 노드 취득

https://dyjung.tistory.com/40

Document.prototype.querySelector("CSS 선택자")

Element.prototype.querySelector("CSS 선택자")

  • 인수로 전달된 CSS 선택자를 만족시키는 첫번째 요소를 탐색하여 반환한다.
  • 선택자를 만족시키는 요소가 존재하지 않는 경우 null을 반환한다.
  • 인수로 전달한 CSS 선택자가 문법에 맞지 않는 경우 DOMException 에러를 발생시킨다.
<!DOCTYPE html>
<html lang="en">
  <body>
    <ul>
      <li id="apple">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
    </ul>
    <script>
      const $elem = document.querySelector("#banana")
      console.log($elem)
      //<li id="banana">...</li>

      const $error = document.querySelector(".#s")
      console.log($error) 
			//Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '.#s' is not a valid selector.
    </script>
  </body>
</html>

Document.prototype.querySelectorAll("CSS 선택자")

Element.prototype.querySelectorAll("CSS 선택자")

  • 인수로 전달된 CSS 선택자를 만족시키는 모든 요소를 NodeList 객체로 반환한다.
  • 선택자를 만족시키는 요소가 존재하지 않는 경우 빈 NodeList를 반환한다.
  • 인수로 전달한 CSS 선택자가 문법에 맞지 않는 경우 DOMException 에러를 발생시킨다.
<!DOCTYPE html>
<html lang="en">
  <body>
    <ul>
      <li id="apple">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
    </ul>
    <script>
      const $elems = document.querySelectorAll('ul > li');
      console.log($elems.forEach(e=>console.log(e)))
    </script>
  </body>
</html>
  • NodeList는 forEach 메서드를 제공한다.
  • 성능상 getElementsBy***보다 떨어지지만, 일관된 방식으로 요소 노드를 취득하는 장점이 있다.

39.2.5 특정 요소 노드를 취득할 수 있는지 확인

Element.prototype.matches("CSS선택자")

  • 인수로 전달한 CSS 선택자를 통해 특정 노소를 취득할 수 있는지를 확인한다.
  • 이벤트 위임 등에서의 조건문에 주로 사용된다.

getElementsByClassName 등은 HTMLCollection을 반환한다.
querySelectorAll 이나 Node 객체의 프로퍼티는 NodeList를 반환한다.

HTMLCollection은 DOM의 현재 상태를 실시간으로 반영(live)하는 반면,
NodeList는 일반적으로 반환된 시기의 상태를 반영(non-live)한다.

둘 모두 유사 배열 객체이며, Array() 생성자를 통해 배열로 변환하여 사용하는 것이 코딩 컨벤션을 유지하는 데에 바람직하다.

39.2.6 HTMLCollection과 NodeList

  • 두 객체 모두 for…of로 순회할 수 있다.
  • Array() 혹은 스프레드 문법을 사용하여 배열로 변환할 수 있다.
  • 인덱스를 통해 요소에 접근할 수 있다.

HTMLCollection

  • getElementsBy*** 메서드를 통해 반환된다.
  • 노드 객체의 상태 변화를 실시간으로 반영한다.(live)

NodeList

  • querySelectorAll 메서드를 통해 반환된다.
  • 실시간으로 노드 객체의 상태 변경을 반영하지 않는다. (non-live)
  • 예외적으로, childNodes 프로퍼티는 NodeList(live)를 반환한다.
  • forEach, item, entries, keys, values 메서드를 제공한다.

이에 따라 HTMLCollection, NodeList 모두 배열로 변환하여 사용하는 것이 권장된다.

39.3 노드 탐색 (traversing)

DOM은 트리의 형태로 구성되어 있으며,
이를 탐색하기 위해 각 노드는 부모/형제/자식 노드에 대한 정보를 프로퍼티로 지니고 있다.

Node 객체에서 제공하는 previousSibling 등의 프로퍼티는 텍스트 노드, 주석 노드 등을 포함하여 탐색하고,
Element 객체에서 제공하는 previousElementSibling 등의 프로퍼티는 요소 엘리먼트에 한하여 탐색한다.

특정 노드를 취득한 후,

해당 노드를 DOM Tree 상에서 탐색할 수 있는 프로퍼티가 제공된다.

39.3.1 공백 텍스트 노드

HTML 문서에서의 스페이스, 탭, 엔터 등을 입력하면 공백 텍스트 노드가 추가된다.

DOM Tree를 순회할 때에, 해당 공백 텍스트 노드가 DOM Tree에 포함되어 있음에 유의해야 한다.

39.3.2 자식 노드 탐색

childNodes, firstChild, lastChild 는 공백 텍스트 노드를 포함하여 탐색/반환한다.
children, firstElementChild, lastElementChild는 공백 텍스트 노드를 제외한 요소 노드만을 탐색/반환한다.

Node.prototype.childNodes : 자식 노드를 모두 탐색하여 NodeList(Live)에 담아 반환한다.

Element.prototype.children : 자식 노드 중 요소 노드를 탐색하여 HTMLCollection에 담아 반환한다. (공백 텍스트 노드가 제외된다.)

Node.prototype.firstChild : 첫 번째 자식 노드를 반환한다.

Element.prototype.firstElementChild : 첫 번째 자식 요소 노드를 반환한다.

Node.prototype.lastChild : 마지막 자식 노드를 반환한다.

Element.prototype.lastElementChild : 마지막 자식 요소 노드를 반환한다.

<!DOCTYPE html>
<html lang="en">
  <body>
    <ul id="fruits">
      <li id="apple">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
      <!-- 주석 -->
    </ul>
    <script>
      const $elems = document.getElementById("fruits")

      //childNode는 공백텍스트와 주석 노드가 포함되어 반환된다.
      console.log($elems.childNodes)
      //NodeList(9) [text, li#apple, text, li#banana, text, li#orange, text, comment, text]

      console.log($elems.children)
      //HTMLCollection(3) [li#apple, li#banana, li#orange, apple: li#apple, banana: li#banana, orange: li#orange]
    </script>
  </body>
</html>

39.3.3 자식 노드 여부 확인

Node.prototype.hasChildNodes()

자식 노드 여부에 따라 true/false를 반환한다.

공백 텍스트 노드 역시 포함된다.

<!DOCTYPE html>
<html lang="en">
  <body>
    <ul id="fruits">
    </ul>
    <script>
      const $elem = document.getElementById("fruits")
      console.log($elem.hasChildNodes()) //true (공백 텍스트 문자가 있음.)
    </script>
  </body>
</html>

39.3.4 요소 노드의 텍스트 노드 탐색

일반적으로 텍스트 노드는 요소 노드의 첫번째 자식이다.

따라서 Node.prototype.firstChild를 통해 접근할 수 있다.

<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="fruits">과일</div>
    <script>
      const $elem = document.getElementById("fruits")
      console.log($elem.firstChild) //"과일"
    </script>
  </body>
</html>

39.3.5 부모 노드 탐색

Node.prototype.parentNode

부모 요소 노드가 반환된다.

(텍스트 노드, 주석 노드 등이 부모 요소가 되는 경우는 없음.)

39.3.6 형제 노드 탐색

‘부모가 같은 형제 노드’를 반환받을 수 있다.

프로퍼티설명
Node.prototype.previousSibling이전 형제 노드를 탐색하여 반환한다.

텍스트 노드, 요소 노드 등이 포함된다.
어트리뷰트 노드는 부모가 같은 노드가 아니기에 반환되지 않는다. |
| Node.prototype.nextSibling | 다음 형제 노드를 탐색하여 반환한다. |
| Element.prototype.previousElementSibling | 자신의 이전 형제 요소 노드를 탐색하여 반환한다. |
| Element.prototype.nextElementSibling | 자신의 다음 형제 요소 노드를 탐색하여 반환한다. |

39.4 노드 정보 취득

Node.prototype.nodeType

노드 타입을 나타내는 상수를 반환한다.

노트 타입 상수는 Node에 정의되어 있다.

노드 타입반환값
Node.ELEMENT_NODE1
Node.ATTRIBUTE_NODE2
Node.TEXT_NODE3
Node.CDATA_SECTION_NODE4
Node.PROCESSING_INSTRUCTION_NODE7
Node.COMMENT_NODE8
Node.DOCUMENT_NODE9
Node.DOCUMENT_TYPE_NODE10
Node.DOCUMENT_FRAGMENT_NODE11

Node.ENTITY_REFERENCE_NODE (5), Node.ENTITY_NODE (6), Node.NOTATION_NODE(12)는 사용하지 않음(deprecated).

Node.prototype.nodeName

노드의 이름을 문자열로 반환한다.

  • 요소 노드 : 태그 이름을 대문자로 반환.
  • 텍스트 노드 : 문자열 “#text”를 반환
  • 문서 노드 : 문자열 “#document”를 반환

39.5 요소 노드의 텍스트 조작

39.5.1 nodeValue

  • nodeType과 nodeName은 읽기 전용
  • nodeValue는 getter와 setter로 이루어진 접근자 프로퍼티 - 따라서 재할당이 가능함.

nodeValue는 텍스트 노드에 대해서 사용이 가능하다.

(텍스트 노드가 아닌 노드는 null을 반환한다.)

<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="fruits">과일</div>
    <script>
      const $elem = document.getElementById("fruits")
			console.log($elem.nodeValue) //null

			const $textNode = document.getElementById("fruits").firstChild;
			console.log($textNode.nodeValue) //"과일"

			$textNode.nodeValue = "fruits"
			console.log($textNode.nodeValue) //"fruits"
    </script>
  </body>
</html>

39.5.2 textContent

Node.prototype.textContent

요소 노드의 텍스트와 모든 자손 노드의 텍스트를 모두 취득하거나 변경한다.

childeNodes 프로퍼티가 반환한 노든 노드들의 텍스트 노드이 값, 텍스트를 모두 반환한다.

<!DOCTYPE html>
<html lang="en">
  <body>
    <ul id="fruits">
      <li id="apple">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
      <!-- 주석 -->
    </ul>
    <script>
      const $elems = document.getElementById("fruits")

      //childNodes 프로퍼티의 모든 텍스트(공백 등)를 반환한다.
      console.log($elems.textContent)
      
      //textContent의 setter는 html을 파싱하지 않는다.
      $elems.textContent = `<li id="apple">Apple</li>`
    </script>
  </body>
</html>

      Apple
      Banana
      Orange

39.6 DOM 조작 (manipulation)

새로운 노드를 DOM에 추가하거나, 기존 노드를 삭제/교체하는 것을 말한다.

이때 리페인트/리플로우 등으로 성능에 영향을 미칠 수 있다.

39.6.1 innerHTML

Element.prototype.innerHTML

setter와 getter를 모두 가진 접근자 프로퍼티로,

HTML 마크업을 취득하거나 변경할 수 있다.

Node.prototype.textContent와 달리 setter가 문자열을 HTML로 파싱한다.

<!DOCTYPE html>
<html lang="en">
  <body>
    <ul id="fruits">
      <li id="apple">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
      <!-- 주석 -->
    </ul>
    <script>
      const $elems = document.getElementById("fruits")

      //콘텐츠 요소 내의 모든 HTML 내용을 반환한다.
      console.log($elems.innerHTML)

      //innerHTML은 문자열을 HTML로 파싱한다.
      $elems.innerHTML = `<li id="apple">Apple</li>`
    </script>
  </body>
</html>

https://hianna.tistory.com/483

XSS 공격(Cross-Site Scripting Attacks)

innerHTML을 통해 script 태그, 이벤트 등을 삽입하여 클라이언트를 조작하는 것.

  1. innerHTML을 이용해 <script>태그 삽입 - HTML5 에서는 막힘
<script>
	document.getElementById('foo').innerHTML 
		= `<script> alert(document.cookie) </script>`
<script>
  1. innerHTML을 이용해 에러 이벤트를 삽입
<script>
	document.getElementById('foo').innerHTML
		= `<img src="x" onerror="alert(document.cookie)">`
<script>
  1. 난독화를 이용한 스크립트 태그 삽입
゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) ['c']+ ((゚ー゚) + (o^_^o))+ ((゚ー゚) + (o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ ((゚ー゚) + (o^_^o))+ (゚Д゚) [゚Θ゚]+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) ['c']+ ((o^_^o) - (゚Θ゚))+ (゚Д゚) .゚ー゚ノ+ (゚Д゚) .゚ー゚ノ+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) ['c']+ ((゚ー゚) + (o^_^o))+ (o^_^o)+ (゚Д゚) ['c']+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ ((゚ー゚) + (゚ー゚))+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚) ['c']+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) ['c']+ ((゚ー゚) + (o^_^o))+ ((゚ー゚) + (゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ (o^_^o)+ (゚Д゚) .゚ー゚ノ+ ((゚ー゚) + (゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚) ['c']+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ ((o^_^o) - (゚Θ゚))+ (゚Д゚) .゚Д゚ノ+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (o^_^o))+ (o^_^o)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');

XSS 공격을 막기 위한 방법으로, 일부 스크립트 태그만 허용하는 화이트 리스트 방식으로 필터를 거는 등의 원리를 이용한 라이브러리로 XSS 공격을 막는 것이 좋다.

39.6.2 insertAdjacentHTML

Element.prototype.insertAdjacentHTML(position, DOMString)

첫번째 인자로 전달할 수 있는 position은 "beforebegin", "afterbegin", "beforeend", "afterend" 네 가지 이다.

두번째 인자로 전달하는 String을 HTML로 파싱하여 해당하는 position에 삽입한다.

  • 자식 요소를 모두 변경하는 innerHTML과 달리, 특정 위치에 자식 요소를 추가한다는 점에서 차이가 있다.
		<script>
      const $elems = document.getElementById("fruits")

      $elems.insertAdjacentHTML('beforeend', '<li id="kiwi">Kiwi</li>')
    </script>

HTML 요소를 추가하는 방법은
createElement 메서드로 새로운 요소를 생성하고,
appendChild를 통해 해당 요소에 textNode, 자식 요소를 추가한 후,
DOM 트리에 존재하고 있는 요소에 appendChild를 통해 삽입해준다.

요소 노드 생성

Document.prototype.createElement('tagName')

const $li = document.createElement('li');

텍스트 노드 생성

Document.prototype.createTextNode(text)

const textNode = document.createTextNode('Banana');

요소 노드에 자식 노드 삽입

Node.prototype.appendChild(childNode)

$li.appendChild(textNode);

요소 노드를 DOM에 추가

$ul.appendChild($li);

39.6.4 복수의 노드 생성과 추가

DOM에 요소 노드를 추가하면, 리플로우 과정이 일어나 성능에 영향을 미친다.
여러 요소 노드를 삽입할 때에는 연산이 끝난 후, 한 번에 노드를 삽입하는 것이 바람직한데, 이 때 documentFragment를 사용할 수 있다.

여러 개의 노드를 생성하고 추가할 때에는 documentFragment를 생성하여 노드를 추가한 후, 이를 DOM에 반영한다.

한 번의 리플로우/리페인트가 실행된다.

const $fruits = document.getElementById('fruits');
const $fragment = document.createDocumentFragment();
['Apple', 'Banana', 'Orange'].forEach(text => {
	// 요소 노드 생성
	const $li = document.createElement('li');
	
	// 텍스트 노드 생성
	const textNode = document.createTextNode(text);
	
	// 텍스트 노드를 li 태그의 자식 요소로 추가
	$li.appendChild(textNode);

	// li 요소 노드를 documentFragment의 자식 요소로 추가
	$fragment.appendChild(li) 
});

// 돔에 documentFragment를 추가
$fruits.appendChild($fragment)

39.6.5 노드 삽입

Node.prototype.appendChild(노드) : 인수로 전달받은 노드를 마지막 자식으로 추가

Node.prototype.insertBefore(newNode, childNode) : 첫번째로 전달받은 노드를, 두번째로 전달받은 자식노드의 앞에 삽입한다.

39.6.6 노드 이동

취득한 노드를 다른 위치에 삽입하면 노드가 이동된다.

<script>
    const $fruits = document.getElementById('fruits');

    const [$apple, $banana, ] = $fruits.children;
    
    $fruits.appendChild($apple); 
    $fruits.insertBefore($banana, $fruits.lastElementChild)
</script>

39.6.7 노드 복사

Node.prototype.cloneNode([deep: true | false])

인자가 false거나 없으면 얕은 복사를 하여 해당 노드만 복사한다.

인자가 true이면, 깊은 복사를 통해 자식노드도 포함하여 복사한다.

39.6.8 노드 교체

Node.prototype.replaceChild(newChild, oldChild)

39.6.9 노드 삭제

Node.prototype.removeChild(child)

39.7 어트리뷰트

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

  • 어트리뷰트 노드 : HTML 요소의 어트리뷰트를 지시하는 노드
  • NamedNodeMap : 어트리뷰트 노드의 참조가 담긴 유사 배열 객체. attributes 프로퍼티로 접근할 수 있다.
<script>
    const $elem = document.getElementById("apple")
    console.log($elem.attributes)
</script>
NamedNodeMap
0: id
1: style
2: value

39.7.2 HTML 어트리뷰트 조작

Element.prototype.getAttribute(attributeName)

Element.prototype.setAttribute(attributeName, attributeValue)

<!DOCTYPE html>
<html lang="en">
  <body>
    <input id="user" type="text" value="ungmo2" />
    <script>
      const $input = document.getElementById("user");
      console.log($input.getAttribute("value");); //ungmo2

      $input.setAttribute("value", "foo");
      console.log($input.getAttribute("value")); //
    </script>
  </body>
</html>

39.7.3 HTML 어트리뷰트 vs. DOM 프로퍼티

  • HTML 어트리뷰트는 초기값을 저장한다.
  • DOM 프로퍼티를 변경하여도 HTML 어트리뷰트는 변화하지 않는다.
<script>
      const $input = document.getElementById("user");
      const inputValue = $input.getAttribute("value");
      console.log(inputValue); // ungmo2
      console.log($input.value); // ungmo2

      $input.value = "foo";
      console.log($input.getAttribute("value")); //ungmo2
      console.log($input.value); //foo
</script>

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

**data 속성**
https://riviere.tistory.com/108
HTML5에서 추가된 속성
HTML 요소에서 속성명이 ’data-’로 시작한다.
브라우저는 ‘data-’ 속성과 어떤 상호작용도 하지 않으며,
개발에 필요한 데이터를 특정 요소에 저장하고 싶은 경우 자유롭게 사용할 수 있다.

  • HTMLElement.dataset : data 어트리뷰트의 값이 저장된 프로퍼티. DOMStringMap 객체이다. (key-value의 쌍으로 이루어져 있음.)
  • DOMStringMap의 키 값은 ‘data-name’에서 접두어 ‘data-’를 뺀 후, 케밥 케이스를 카멜 케이스로 변환한 값이다. (data-user-id ⇒ userId)
<!DOCTYPE html>
<html lang="en">
  <body>
    <ul class="users">
      <li id="1" data-user-id="7621" data-role="admin">Lee</li>
      <li id="2" data-user-id="9524" data-role="subscriber">Kim</li>
    </ul>
    <script>
      const users = [...document.querySelector(".users").children];
      const user = users.find((user) => user.dataset.userId === "7621");

      // 요소 노드의 dataser 프로퍼티로 접근할 수 있다.
      console.log(user.dataset); // DOMStringMap {userId: '7621', role: 'admin'}

      // DOMStringMap의 값에 접근하거나 재할당할 수 있다.
      user.dataset.role = "member";
      console.log(user.dataset.role); // member
    </script>
  </body>
</html>

39.8 스타일

39.8.1 인라인 스타일 조작

[HTMLElement.prototype.style](http://HTMLElement.prototype.style) 인라인 스타일을 취득, 추가, 변경하는 getter/setter가 있는 접근자 프로퍼티이다.

<!DOCTYPE html>
<html lang="en">
  <body>
    <div style="color: red">Hello World</div>
    <script>
      const $div = document.querySelector("div");

      console.log($div.style); //CSSStyleDeclaration {0: 'color', accentColor: '', additiveSymbols: '', alignContent: '', alignItems: '', alignSelf: '', …}

      $div.style.color = "blue"; // 스타일 변경
      $div.style.backgroundColor = "yellow"; // 스타일 추가
    </script>
  </body>
</html>

39.8.2 클래스 조작

Element.prototype.className : getter와 setter가 모두 존재하는 접근자 프로퍼티. 문자열을 반환한다.

Element.prototype.classList : class 어트리뷰트의 정보를 DOMTokenList로 반환한다.

DOMTokenList는 아래와 같은 메서드를 포함하고 있다.

add(…’className’) : 인자로 전달받은 문자열을 class 어트리뷰트 값으로 추가한다.

remove(…’className’) : 인자로 전달받은 문자열과 일치하는 문자열을 class 어트리뷰트에서 삭제한다.

item(index) : 해당하는 인덱스의 클래스를 반환한다.

contains(className) : 일치하는 클래스가 있는지 boolean으로 반환한다.

replace(old, new) : 첫번째 인수로 전달한 문자열을 두번째 인수로 전달한 문자열로 교체한다.

toggle(className[, force] : 클래스가 존재하면 제거하고, 클래스가 없으면 추가한다. force 값이 true이면 강제로 추가하고, false이면 강제로 제거한다.

39.8.3 요소에 적용되어 있는 CSS 스타일 참조

인라인 스타일이 아닌 스타일을 참조하는 경우 window.getComputedStyle(element[, pseudo])메서드를 사용한다.

두번째 요소로 ":after" 와 같은 의사요소를 지정하는 문자열을 전달할 수 있다.

해당 메서드가 반환하는 CSSStyleDeclaration는 링크 스타일, 임베딩 스타일, 인라인 스타일, 자바스크립트로 적용한 스타일, 상속된 스타일, user agent 스타일 등 모든 스타일이 조합되어 최종적으로 적용된 스타일을 의미한다.

const $box = document.querySelector(".box");

const computedStyle = window.getComputedStyle($box);
console.log(computedStyle); // CSSStyleDeclaration
console.log(computedStyle.width); // 100px

39.9 DOM 표준

DOM은 새로운 표준이 등장할 때 이를 ‘level’로 표현한다.

현재 4개의 레벨이 있다.

DOM Level 1 (1998년10월) : Core,HTML 모듈 정의

DOM Level 2 (2000년11월) : Core,HTML,Views,Events,Style 등 모듈 정의

DOM Level 3 (2004년4월) : Core,Load and Save,Validation,Xpath,Events 모듈 정의

DOM Level 4 (2015년11월) : WHATWG에서 추가한 표준

profile
천재가 되어버린 박제를 아시오?

0개의 댓글