DOM

5o_hyun·2022년 3월 11일
0
post-thumbnail

DOM이란?

DOM ( Document Object Model )은 HTML문서의 계층적구조와 정보를 표현하며 이를 제어할수 있는 API, 즉 프로퍼티와 메서드를 제공하는 트리자료구조다.

각기다른 문서(html,javascript등)를 제어할수있는 자료구조라고 생각하면 편하다.

1. 노드


HTML요소는 렌더링엔진에 의해 파싱되어 DOM을 구성하는 요소 노드 객체로 변환된다.
이때 HTML요소의 어트리뷰트는 어트리뷰트 노드로, 텍스트콘텐츠는 텍스트노드로 변환된다.

따라서 HTML문서는 HTML요소들의 집합으로 이뤄지며 HTML요소는 중첩관계를 갖는다.
이때 HTML요소간에는 중첩관계에의해 계층적인 부자관계가 형성된다. 이를 반영하여 HTML요소를 객체화한 모든 노드객체들을 트리자료구조로 구성한다.

다음과 같이 노드 객체들로 구성된 트리 자료구조를 DOM이라 한다.
노드객체의 트리형태로 구조화되어있기 때문에 DOM을 DOM트리라고 부르기도 한다.

  • 문서 노드 : 최상위에 존재하는 루트노드로서 document객체를 가리킨다.
  • 요소 노드 : HTML요소를 가리키는 객체다. 문서의 구조를 표현한다고 볼 수 있다.
  • 어트리뷰트 노드 : 요소노드의 어트리뷰트
  • 텍스트 노드 : 요소노드의 텍스트

DOM은 HTML문서의 계층적 구조와 정보를 표현하는것은 물론 노드객체의 종류에따라 필요한 기능을 프로퍼티와 메서드 집합인 DOM API로 제공한다.
이 DOM API를 통해 HTML의 구조나 내용 또는 스타일 등을 동적으로 조작할 수 있다.

2. 요소노드취득

HTML구조나 내용 또는 스타일 등을 동적으로 조작하기 위해서는 먼저 요소노드를 취득해야한다.

id를 이용한 요소노드취득

document.getElementById 메서드는 인수로 전달한 id 어트리뷰트 값을 갖는 하나의 요소노드를 탐색하여 반환한다.
id값은 HTML문서내에 유일한 값이여야한다.
따라서 언제나 단 하나의 요소노드를 반환하며, 존재하지 않을경우 null을 반환한다.

    <ul>
        <li id="apple">Apple</li>
        <li id="banana">Banana</li>
        <li id="orange">Orange</li>
    </ul>
    <script>
        const a = document.getElementById('banana');
        a.style.color='red';
        // 변수 a 에 id가 banana인거를 담아, dot notation으로 칼라 변경
    </script>

class를 이용한 요소노드취득

document.getElementsByClassName메서드는 인수로 전달한 class어트리뷰트값을 갖는 모든 요소노드를 탐색해 반환한다.
클래스는 id랑 다르게 여러개다.

    <ul>
        <li class="fruit apple">Apple</li>
        <li class="fruit banana">Banana</li>
        <li class="fruit orange">Orange</li>
    </ul>
    <script>
        const a = document.getElementsByClassName('fruit');
        [...a].forEach(a => a.style.color='red');

        const b = document.getElementsByClassName('fruit banana');
        [...b].forEach(b => b.style.color = 'blue');
    </script>

document.getElementsByClassName으로 검색뿐 아니라 querySelector를 이용하는 방법도 있다.

document.querySelector : 클래스가 여러개일때 가장 먼저 검색되는 태그를 리턴
document.querySelectorAll : 유사배열 객체로 여러개가 담겨나옴. 따라서 접근할때 인덱스로 접근하면 된다.

    <ul>
        <li class="fruit apple">Apple</li>
        <li class="fruit banana">Banana</li>
        <li class="fruit orange">Orange</li>
    </ul>
    <script>
        const a = document.querySelectorAll('ul > li');
        console.log(a); // NodeList(3) [li.fruit.apple, li.fruit.banana, li.fruit.orange]
        a.forEach(a =>a.style.color='red');

        const b = document.querySelector('.fruit');
        b.style.color='blue';
    </script>

css선택자 문법을 사용하는 querySelector, querySelectorAllgetElementById, getElementsByClassName보다 다소 느리다는 단점이 있다.
하지만 css선택자 문법을 사용해 좀 더 구체적인 조건으로 요소노드를 취득할수있다는 장점이 있다.

따라서,id로 취득시 getElementById를 사용하고, class로 취득시 querySelector, querySelectorAll 을 사용하는것을 권장한다.

3. 노드 탐색

요소노드를 취득한 다음, 취득한 요소노드를 기점으로 DOM트리의 노드를 옮겨다니며 부모,자식노드등을 탐색한다.

1. 자식노드탐색

childNodes : 자식노드를 모두 탐색해 NodeList에 담아 반환한다. 요소노드뿐아니라 텍스트노드도 포함되어있다.
children : 자식노드 중 요소노드만 모두 탐색에 HTMLCollection에 담아 반환한다. 텍스트노드포함x
firstChild : 첫번째 자식노드 반환 , 요소노드 혹은 텍스트노드 반환
lastChild : 마지막 자식노드 반환 , 요소노드 혹은 텍스트노드 반환

firstElementChild : 첫번째 자식노드의 요소노드만 반환
lastElementChild : 마지막 자식노드의 요소노드만 반환

    <ul id = "fruits">
        <li class="fruit apple">Apple</li>
        <li class="fruit banana">Banana</li>
        <li class="fruit orange">Orange</li>
    </ul>
    <script>
        const a = document.getElementById('fruits');
        console.log(a);
        console.log(a.childNodes); // NodeList(7) [text, li.fruit.apple, text, li.fruit.banana, text, li.fruit.orange, text]
        console.log(a.children); // HTMLCollection(3) [li.fruit.apple, li.fruit.banana, li.fruit.orange]
        console.log(a.firstChild); // #text
        console.log(a.lastChild); // #text

        console.log(a.firstElementChild); // <li class="fruit apple">Apple</li>
        console.log(a.lastElementChild); // <li class="fruit orange">Orange</li>
    </script>

2. 부모노드탐색

parentNode를 사용해 부모노드를 검색한다.

    <script>
       const a = document.querySelector('.banana');
       console.log(a.parentNode); // <ul>~
    </script>

3. 형제노드탐색

부모노드가 같은 형제노드 탐색 방법
preciousSilbling : 형제노드중 자신의 이전 형제노드를 검색해 반환
nextSilbling : 형제노드중 자신의 다음 형제노드를 검색해 반환

//더공부필요

4. 노드정보취득

노드객체에 대한 정보를 취득할 때
nodeType : 숫자를 반환하는데 숫자에따라 어떤 종류인지 알려줌
요소노드일경우 1 / 텍스트노드일경우 3 / 문서노드일경우 9
nodeName : 노드의 이름을 문자열로 반환
요소노드일경우 UL,LI등 대문자 / 텍스트노드일경우 #text / 문서노드일경우 #document

    <div id="foo">hello</div>
    <script>
       const a = document.getElementById('foo');
       console.log(a.nodeType); // 1
       console.log(document.nodeType); // 9

       console.log(a.nodeName); // DIV
       console.log(document.nodeName); // #document
    </script>

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

요소노드의 textContent 프로퍼티에 문자열을 할당하면, 문자열에 HTML마크업이 포함되어 있더라도 문자열 그대로 인식되어 텍스트로 취급된다. 즉, HTML마크업이 파싱되지 않는다.

    <div id="foo">hello</div>
    <script>
       const a = document.getElementById('foo');
       a.textContent = 'bye'; // hello --> bye로 바뀜
    </script>

textContent와 유사한 동작을 하는 innerHTML이 있지만, innerHTML 프로퍼티에 할당하는것은 크로스 사이트 스크립트공격 (XSS)에 취약하므로 위험하다.
따라서 textContent를 사용하자

6. DOM 조작

노드 생성과 추가

지금까지는 HTML 문자열을 파싱하여 노드를 생성하고 DOM에 반환했지만,
DOM은 노드를 직접 생성.삽입,삭제,치환하는 메서드도 제공한다.

createElement : 요소 노드를 생성하여 반환
createTextNode : 텍스트노드를 생성하여 반환
appendChild : 제일 마지막 자식노드로 추가

    <ul id="fruit">
        <li>Apple</li>
    </ul>
    <script>
    	// 1. 기준이 되는 루트노드 취득
       const fruits = document.getElementById('fruit');
       
       // 2. li 요소노드 생성
       const banana = document.createElement('li');
       
       // 3. 텍스트노드 Banana생성
       const textNode = document.createTextNode('Banana');
       
       // 4. 텍스트노드를 아까만든 li 자식노드에 추가
       banana.appendChild(textNode);
       
       // 5. li요소노드를 #fruits의 자식노드에 추가 
       fruits.appendChild(banana);
    </script>

textContent와 createTextNode 의 차이점
createTextNode는 appendChild를 하고싶을때,
textContent는 그렇지 않을 때의 삽입,수정의 차이라고 생각하면 편하다.

복수의 노드 생성과 추가

아까 한것에서 forEach를 돌리면 됨

    <ul id="fruit"></ul>
    <script>
    	// 1. 기준이 되는 루트노드 취득
       const fruits = document.getElementById('fruit');
       
       // 2. forEach 돌려
       ['Apple','Banana','Orange'].forEach(text => {
           // 3. li 요소노드 생성
           const liGroup = document.createElement('li');
           // 4. 텍스트노드 생성
           const textNode = document.createTextNode(text);
           // 5. 텍스트노드를 li요소노드의 자식노드로 추가
           liGroup.appendChild(textNode);
           // 6. li요소노드를 fruits의 자식노드로 추가
           fruits.appendChild(liGroup);
       })
    </script>

7. 어트리뷰트

앞서 했듯이 노드를 취득한 후, 어트리뷰트를 조작할 수 있다.
HTML어트리뷰트는 HTML요소의 시작태그에 어트리뷰트이름="어트리뷰트값"형식으로 정의한다.

getAttribute : HTML 어트리뷰트 값 참조
setAttribute : HTML 어트리뷰트 값 변경
hasAttribute : HTML 어트리뷰트 존재하는지 확인
removeAttribute : HTML 어트리뷰트 삭제

    <input id="user" type="text" value="ungmo2">
    <script>
       const input = document.getElementById('user');
       
       // value 어트리뷰트 취득
       const inputValue = input.getAttribute('value');
       console.log(inputValue); // ungmo2

       // value 어트리뷰트 변경
       input.setAttribute('value','foo');
       console.log(input.getAttribute('value')); // foo

		// value 어트리뷰트 있는지 확인하고 있으면 삭제
       if(input.hasAttribute('value')){
           input.removeAttribute('value');
       }
       console.log(input.hasAttribute('value')); // false
    </script>

8. 스타일

인라인 스타일 조작

HTMLElement.prototype.style 프로퍼티는 요소노드의 인라인스타일을 취득하거나 추가 또는 변경한다.

단, px, em, % 등의 크기 단위가 필요한 width프로퍼티에 값을 할당할 때 단위를 생략하면 해당 CSS프로퍼티는 적용되지 않는다.

    <div style="color:red;">hello</div>
    <script>
       const box = document.querySelector('div');

       // 인라인스타일 취득
       console.log(box.style);

       // 인라인스타일 추가
       box.style.color - 'blue';
       box.style.background = 'grey';
       box.style.width = '100px';
    </script>

클래스 조작

class어트리뷰트를 조작하려면 classList가 대응된다.
class어트리뷰트에 대응하는 DOM프로퍼티는 class가 아니라 classList다. 자바스크립트에서 class는 예약어이기 때문이다.

Element.prototype.classList프로퍼티는 class 어트리뷰트 정보를 담은 DOMTokenList 객체를 반환한다.
DOMTokemList객체는 다음과 같은 유용한 메서드들을 제공한다.

add : 인수로 전달한 1개이상의 문자열을 class어트리뷰트값으로 추가한다.
remove : 인수로 전달한 1개이상의 문자열과 일치하는 클래스를 class 어트리뷰트에서 삭제한다.
contain : 인수로 전달한 문자열과 일치하는 클래스가 class 어트리뷰트에 있는지 확인한다.

profile
학생 점심 좀 차려

0개의 댓글