15. DOM

DOM (Document Object Model)

브라우저의 렌더링 엔진이 HTML을 브라우저가 이해할 수 있는 구조로 메모리에 적재를 하는데 이를 DOM이라고 합니다. HTML의 중첩된 태그로 구성된 계층적인 구조가 DOM에서는 객체로 만들어지고 부모 자식 형제 관계의 트리구조로 이루어집니다.

<html>
  <head>
    <style>
      .item  { color: #999; }
    </style>
  </head>
  <body>
    <div>
      <h1>HTML 입니다</h1>
      <ul>
        <li class="item">Appear</li>
      </ul>
    </div>
  </body>
</html>

HTML -> DOM (이런식으로 DOM Tree객체가 만들어집니다)

{
   document: {
      html: {
        head: {},
        body: {
          div : {
              h1 :{},
              ul :{
                  li :{}
              }
          }
        }
      }
    }
 }

Node

DOM Tree는 Node들로 구성되어있습니다. Node에 대해서 알아보겠습니다.

  • Document Node: 트리의 최상위 노드입니다. Element Node와 Text Node에 접근하려면 Document를 통해야합니다.

  • Element Node: , 같은 태그들을 의미합니다.

  • Text Node: 안녕하세요 Olaf 입니다. 에서 “안녕하세요 Olaf 입니다.” 가 Text Node 입니다.

DOM 선택

document.getElementById(id)

id 속성값으로 요소 노드를 선택해 올 수 있습니다.

<div id="root"></div>
var div = document.getElementById('root'); 
console.log(div); // <div id="root"></div>
console.log(div.__proto__);           // HTMLDIVElement
console.log(div.__proto__.__proto__); // HTMLElement
console.log(div.__proto__.__proto__.__proto__);           // Element
console.log(div.__proto__.__proto__.__proto__.__proto__); // Node

document.querySelector(cssSelector)

CSS Selector를 이용해서 노드를 선택할 수 있습니다. 복수개가 선택된 경우, 첫번째 요소만 반환합니다.

<ul class="list_item">
    <li id="item_1">item1</li>
    <li class="item_2">item2</li>
</ul>

Tag를 통해서 찾기

var ul = document.querySelector('ul'); 
console.log(ul);   
/*
선택한 ul이 들어있는거 보이시죠? 
<ul class="list_item">
    <li id="item_1">item1</li>
    <li class="item_2">item2</li>
</ul>
*/
var li = ul.querySelector('li'); 
console.log(li); // <li>item1</li> 중복된다면 첫번째 요소만 가져옵니다.

var li = ul.querySelector(‘li’) 이부분을 보시면 querySelector 로 찾아놓은 ul의 li를 선택하였습니다. 만약 ul을 통하지않고 (‘ul li’)를 하게된다면 매 실행 때 마다 ul을 찾아야합니다. ul을 최초한번 찾아 변수에 담아놓고 저장된 ul을 통해서 찾습니다.

Class를 통해서 찾기

var li = document.querySelector('.item_2');  
console.log(li); // <li class="item_2">item2</li>

Id를 통해서 찾기

var li = document.querySelector('#item_1');  
console.log(li); // <li id="item_1">item1</li>

querySelector는 중복되는 요소들은 가져오지 못합니다.

document.querySelectorAll(cssSelector)

querySelector는 중복된 값이 있다면 첫번째 요소만 반환했지만 All을 사용하게되면 중복된 요소들도 가져올 수 있어요. querySelector와 사용법은 똑같습니다.

<div>
    <span class="item">1</span>
    <span class="item">2</span>
    <span class="item">3</span>
</div>
var elems = document.querySelectorAll('.item');
console.log(elems); // [span.item, span.item, span.item]

document.getElementsByClassName(class)

해당 Class 값을 가진 요소 노드를 다 가져올 수 있습니다. 주의 하실점은 IE9 이상에서만 동작합니다.

<div>
    <span class="item">1</span>
    <span class="item">2</span>
    <span class="item">3</span>
</div>
var elems = document.getElementsByClassName('item');
console.log(elems); // [span.item, span.item, span.item]

document.getElementsByTagName(tagName)

태그 이름으로도 요소 노드를 가져올 수 있습니다.

<ul>
    <li>item1</li>
    <li>item2</li>
    <li>item3</li>
</ul>
var liList = document.getElementsByTagName('li');
console.log(liList); // [li, li, li]

DOM 탐색

parentNode

선택된 요소의 부모 노드를 탐색합니다.

<ul class="list_item">
    <li id="item">1</li>
    <li>2</li>
    <li>3</li>
</ul>
var li = document.getElementById('item'); // id 속성을 이용해서 선택
console.log(li.parentNode) // <ul class="list_item"></ul>

Children

자식들을 반환해줍니다.

<ul class="list_item">
    <li id="item_1">1</li>
    <li>2</li>
    <li>3</li>
</ul>
var ul = document.querySelector('ul'); // ul을 찾아서 캐싱
console.log(ul.children); // (3)[li#item_1, li, li, item_1: li#item_1]

Child

  • firstChild : 첫번째 자식 노드를 탐색 Text Node 반환

  • lastChild : 마지막 자식 노드를 탐색 Text Node 반환

  • firstElementChild : 첫번째 자식노드를 탐색 Element Node 반환

  • lastElementChild : 마지막 자식 노드를 탐색 Element Node 반환

<ul class="list_item">
    <li id="item">1</li>
    <li>2</li>
    <li>3</li>
</ul>
var ul = document.querySelector('ul'); // ul을 찾아서 캐싱
var first = ul.firstChild;
var last = ul.lastChild;
var firstEl = ul.firstElementChild;
var lastEl = ul.lastElementChild;
console.log(ul);
console.log(first); // 첫번째 요소 #text
console.log(last); // 마지막 요소 #text
console.log(firstEl); // <li id="item">1</li>
console.log(lastEl); // <li>3</li>

hasChildNodes

자식 노드를 가지고 있는지 알 수 있습니다.

<ul class="list_item">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
var ul = document.querySelector('ul'); // ul을 찾아서 캐싱
console.log(ul.hasChildNodes()); // true

childNodes

자식 노드들을 반환해준다. Text Node, Element Node 구분없다.

<ul class="list_item">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
var ul = document.querySelector('ul'); // ul을 찾아서 캐싱
console.log(ul.childNodes()); // [text, li, text, li, text, li, text]

Sibling

형제 노드를 탐색합니다. IE9 이상의 브라우저에서 동작합니다. Text Node로 반환됩니다.

<ul class="list_item">
    <li id="item_1">1</li>
    <li>2</li>
    <li>3</li>
</ul>
var ul = document.querySelector('ul'); // ul을 찾아서 캐싱
var li = document.getElementById('item_1'); 
console.log(li.nextSibling);
console.log(li.previousSibling);

DOM 조작

innerText

텍스트 노드의 값을 바꿀 수 있습니다.

<div id="txt_status">수정전</div>
var div = document.getElementById('txt_status');
div.innerText = '수정후';

className

class의 값을 변경 또는 검색할 수 있어요

<div class="root" id="root">Root</div>
var div = document.getElementById('root');
console.log(div.className); // root
div.className = 'modify'; // 개발자 도구로 확인해보세요 class가 modify로 바뀌었나요?

id

id의 값을 변경또는 검색할 수 있어요

<div class="root" id="root">Root</div>
var div = document.querySelector('.root');
console.log(div.id); // root
div.id = 'modify'; // 개발자 도구로 확인해보세요 id가 modify로 바뀌었나요?

Attribute

  • hasAttribute(attribute) : 속성을 가지고 있는지 확인합니다.

  • getAttribute(attribute) : 속성의 값을 가져옵니다.

  • setAttribute(attribute, value) : 속성의 값을 바꿀 수 있습니다.

  • removeAttribute(attribute) : 속성의 값을 제거할 수 있어요

<div class="root" id="root">안녕하세요</div>
var div = document.getElementById('root');
console.log(div.hasAttribute('class')); // class의 존재여부
console.log(div.getAttribute('class')); // class 속성의 값
div.setAttribute('class', 'modify'); // class 속성의 값을 변경
console.log(div.getAttribute('class')); // 변경된 후의 class 값 
div.removeAttribute('class'); // 클래스 제거 
div.className = 'newClass'; // class 이름 추가
  • attributes : 선택된 태그의 전체 속성을 볼 수 있습니다.

  • clientHeight, clientWidth : scrollbar, margin, border를 제외한 높이와 너비를 반환해 줍니다.

  • scrollHeight, scrollWidth : 스크롤 가능한 범위까지 포함한 태그의 높이와 너비를 반환해 줍니다.

  • offsetHeight, offsetWidth : 태그의 margin만 제외한 높이와 너비를 반환해 줍니다.

style 제어 : https://jsfiddle.net/fbzffnd4/ 참고해주세요

다 외우지 않으셔도 됩니다. 필요할 때 마다 찾아보고 사용하시면 됩니다.

Tag 만들기

실직적인 HTML를 조작하기전에 Element, TextNode 를 생성하는 법을 살펴보고 갈게요

document.createElement

document.createElement(태그이름); 
var div = document.createElement('div');

document.createTextNode

document.createTextNode(텍스트)
var div = document.createElement('div'); // 바로 생성되는 것이 아니라 메모리에 저장을 시켜놓습니다.
var text = document.createTextNode('안녕하세요');  // 바로 생성되는 것이 아니라 메모리에 저장을 시켜놓습니다. 
div.appendChild(text); // appendChild는 뒤에서 배울 자식요소로 주가해주는 함수입니다.

document.createDocumentFragment

처음 HTML이 렌더링될때 CSS에따른 (색상, 위치 등)을 계산하여 렌더 트리를 생성합니다. 생성된걸 토대로 브라우저에 그려줍니다. 우리가 동적으로 만든 태그를 사이에 껴넣으면 어떻게 될까요? 또 다시 위치를 잡아줘야하고 다시 그려줘야 되겠죠? 이 과정을 Reflow와 Repaint이라고합니다.

우리가 div를 100개를 추가한다고 가정할게요. 100번의 반복문을 돌면서 100개의 태그를 추가했습니다. 우리는 반복문이 알아서 추가해주니까 아주편하지만 그것을 계산하고 그리고 과정을 100번을 반복하는 브라우저 입장에서는 힘든 작업입니다.

그래서 DocumentFragment라는 가상의 공간을 만들어서 그곳에 100개를 추가하고 단 한번만 실제 DOM에 추가를 해주는 것입니다. 100번 Reflow와 Repaint이 일어날 것을 한번만 계산하도록 해주는 것이지요

var div = document.createElement('div'); 
var text = document.createTextNode('안녕하세요');  
div.appendChild(text);
var fragment =  document.createDocumentFragment();
var index = 0;
while(++index < 100) {
  fragment.appendChild(div); // appendChild로 fragment에 넣어줍니다.  appendChild는 곧 배울거에요 지금은 그냥 추가하는 용도로 쓰인다는 것만 알아주세요
}

HTML 콘텐츠 조작

  • innerText : 텍스트를 변경 시킬 수 있습니다.

  • innerHTML : 텍스트를 변경 또는 태그를 추가시켜줄 수 있습니다.

<div>수정전</div>
<ul class="list_item">

</ul>
var div = document.querySelector('div');
div.innerText = '수정후';
var ul = document.querySelector('.list_item');
ul.innerHTML = '<li>item추가</li>'

appendChild

<div id="root"></div>
var fragment =  document.createDocumentFragment();
var index = 0;
while(++index < 20) {
  var div = document.createElement('div'); 
  var text = document.createTextNode('안녕하세요');  
  div.appendChild(text);
  fragment.appendChild(div);
}
document.getElementById('root').appendChild(fragment);

removeChild

선택된 자식요소를 삭제합니다.

<ul id="list_item">
    <li>item1</li>
    <li>item2</li>
    <li>item3</li>
</ul>
var ul = document.getElementById('list_item');
console.log(ul.children);
ul.removeChild(ul.children[0]); // children을 이용하면 자식들을 가져올 수 있습니다.

insertBefore

부모.insertBefore(추가, 기준) 을 넣어주시면 기준의 형제태그로 넣을 수 있습니다.

<ul class="list_item">
   <li id="item_1">기준노드</li>
</ul>
var ul = document.querySelector('.list_item');
var li = document.getElementById('item_1');
var newLi = document.createElement('li');
var text = document.createTextNode('추가');
newLi.appendChild(text);
ul.insertBefore(newLi, li);

Last updated