Dom

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

노드

HTML 요소와 노드 객체

트리 자료구조

노드들의 계층구조로 이뤄진다. 부모노드와 자식노드로 구성되어 노드 간 계층적 구조를 표현하는 비선형 자료구조이다.
노드 객체들로 구성된 트리 자료구조를 DOM이라 한다.

노드 객체의 타입

문서노드

DOM 트리의 최상위에 존재하는 루트노드로써 document 객체를 가리킨다. 진입점 역할을 하며 다른 노드에 접근하려면 문서 노드를 통해야한다.

요소노드

HTML 요소를 가리키는 객체이다. 문서의 구조를 표현한다.

어트리뷰트 노드

HTML 요소의 어트리뷰트를 가리키는 객체이다.

텍스트 노드

문서의 정보를 표현한다.

노드 객체의 상속 구조

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

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

요소 노드 취득

id를 이용한 요소 노드 취득

<!DOCTYPE html>
<html>
  <body>
    <ul>
      <li id="apple">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
    </ul>
    <script>
      
      const $elem = document.getElementById('banana');

   
      $elem.style.color = 'red';
    </script>
  </body>
</html>
  • getElementById 메서든느 id어트리뷰트 값을 갖는 요소 노드를 탐색해서 반환한다.

  • 중복된 id 값이 여러개라도 에러가 발생하지 않고 첫 번째 요소를 반환한다.

  • id가 존재하지 않을 경우 null을 반환한다.

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

<!DOCTYPE html>
<html>
  <body>
    <ul>
      <li id="apple">Apple</li>
      <li id="banana">Banana</li>
      <li id="orange">Orange</li>
    </ul>
    <script>
      
      const $elems = document.getElementsByTagName('li');

    .
      [...$elems].forEach(elem => { elem.style.color = 'red'; });
    </script>
  </body>
</html>
  • getElementsByTagName 메서드는 여러 개의 요소 노드 객체를 같는 HTMLCollection 객체를 반환한다.
  • 여러개의 값을 반환하려면 배열이나 객체와 같은 자료구조에 담아 반환한다.
  • HTMLCollection 객체는 유사배열 객체이면서 이터러블이다
  • 모든 요소를 취득하려면 인수에 * 전달한다.
  • 관련 태그가 탐색이 안된다면 빈 객체를 반환한다.

class를 이용한 요소 노드 취득

<!DOCTYPE html>
<html>
  <body>
    <ul>
      <li class="fruit apple">Apple</li>
      <li class="fruit banana">Banana</li>
      <li class="fruit orange">Orange</li>
    </ul>
    <script>
      // class 값이 'fruit'인 요소 노드를 모두 탐색하여 HTMLCollection 객체에 담아 반환한다.
      const $elems = document.getElementsByClassName('fruit');

      // 취득한 모든 요소의 CSS color 프로퍼티 값을 변경한다.
      [...$elems].forEach(elem => { elem.style.color = 'red'; });

      // class 값이 'fruit apple'인 요소 노드를 모두 탐색하여 HTMLCollection 객체에 담아 반환한다.
      const $apples = document.getElementsByClassName('fruit apple');

      // 취득한 모든 요소 노드의 style.color 프로퍼티 값을 변경한다.
      [...$apples].forEach(elem => { elem.style.color = 'blue'; });
    </script>
  </body>
</html>
  • getElementByClassName 메서드도 HTMLCollection 객체를 반환한다.
  • 요소가 존재하지 않는 경우 빈 객체 반환한다.

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

<!DOCTYPE html>
<html>
  <body>
    <ul>
      <li class="apple">Apple</li>
      <li class="banana">Banana</li>
      <li class="orange">Orange</li>
    </ul>
    <script>
      // ul 요소의 자식 요소인 li 요소를 모두 탐색하여 반환한다.
      const $elems = document.querySelectorAll('ul > li');
      // 취득한 요소 노드들은 NodeList 객체에 담겨 반환된다.
      console.log($elems); // NodeList(3) [li.apple, li.banana, li.orange]


      $elems.forEach(elem => { elem.style.color = 'red'; });
    </script>
  </body>
</html>
  • querySelectot 메서드는 CSS 선택자를 만족시키는 요소노드를 탐색하여 NodeList객체를 반환한다.

HTMLCollection과 NodeList

모두 유사 배열 객체이면서 이터러블이다. for...of를 사용할 수 있고 스프레드 문법 사용가능하다.

중요한 특징은 노드 객체의 상태변화를 실시간으로 반영하는 살아있는 객체이다.

HTMLCollection은 언제나 live 객체로 동작한다.
그러나 NodeList의 경우 실시간으로 반영하지 않고 과거의 정적 상태를 유지하는 non-live로 동작하지만 경우에 따라서 live 객체로 동작한다.

HTMLCollection

  • getElementByTagName, getElementByClassName 메서드가 반환한다.
[...$elems].forEach(elem => elem.className = 'blue');
  • 부작용을 피하기 위해 HTMLCollection을 배열로 변환하여 순회한다.

NodeList


const $elems = document.querySelectorAll('.red');


$elems.forEach(elem => elem.className = 'blue');
  • querySelectorAll은 DOM 컬렉션 객체인 NodeList를 반환한다.
  • NodeList 객체는 NodeList.prototype.forEach 메서드를 상속받아 사용할 수 있다.

노드 객체의 상태 변경과 상관없이 안전하게 DOM을 사용하려면 HTMLCollection이나 NodeList 객체를 배열로 변환하여 사용하는 것을 권장한다.

요소 노드의 텍스트 조작

nodeValue

<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello</div>
  </body>
  <script>
    // 1. #foo 요소 노드의 자식 노드인 텍스트 노드를 취득한다.
    const $textNode = document.getElementById('foo').firstChild;

    // 2. nodeValue 프로퍼티를 사용하여 텍스트 노드의 값을 변경한다.
    $textNode.nodeValue = 'World';

    console.log($textNode.nodeValue); // World
  </script>
</html>
  • nodeValue 프로퍼티를 참조하면 노드 객체의 값을 반환한다. 노드 객체의 값이란 텍스트 노드의 텍스트이다.
  • 텍스트 노드가 아닌 노드를 참조하면 null을 반환한다.

textContent

<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello <span>world!</span></div>
  </body>
  <script>
    // #foo 요소 노드의 텍스트를 모두 취득한다. 이때 HTML 마크업은 무시된다.
    console.log(document.getElementById('foo').textContent); // Hello world!
  </script>
</html>
  • textContent 프로퍼티를 참조하면 요소 노드의 콘텐츠 영역 내의 텍스트를 모두 반환한다.

DOM 조작

innerHTML

<!DOCTYPE html>
<html>
  <body>
    <div id="foo">Hello <span>world!</span></div>
  </body>
  <script>
    // #foo 요소의 콘텐츠 영역 내의 HTML 마크업을 문자열로 취득한다.
    console.log(document.getElementById('foo').innerHTML);
    // "Hello <span>world!</span>"
  </script>
</html>
  • 요소 노드의 콘텐츠 영역내에 포함된 모든 HTML 마크업을 문자열로 반환한다.
  • 크로스 사이트 스크립팅 공격에 취약한 단점이 있다.
  • 복잡하지 않은 요소를 새롭게 추가할 때 유용하지만 위치를 지정해야한다면 사용하지 않는것이 좋다.

insertAdjacentHTML 메서드

<!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>
  • 기존 요소를 제거하지 않으면서 위치를 지정해 새로운 요소를 삽입한다.

노드의 생성과 추가

요소 노드 생성

const $li = document.createElement('li');
  • createElement 메서드는 요소드를 생성해서 반환한다. 태그 이름을 나타내는 문자열을 인수로 전달한다.

텍스트 노드 생성

const textNode = document.createTextNode('Banana');
  • createTextNode 메서드는 텍스트 노드를 생성하여 반환한다. 매개변수에는 텍스트 노드에 사용할 문자열을 인수로 전달한다.

텍스트 노드를 요소 노드의 자식 노드로 추가

$li.appendChild(textNode);

노드 삽입

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li>Apple</li>
      <li>Banana</li>
    </ul>
  </body>
  <script>
    // 요소 노드 생성
    const $li = document.createElement('li');

    // 텍스트 노드를 $li 요소 노드의 마지막 자식 노드로 추가
    $li.appendChild(document.createTextNode('Orange'));

    // $li 요소 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
    document.getElementById('fruits').appendChild($li);
  </script>
</html>
  • appendChild 메서드는 인수로 전달한 노드를 appendChild 메서드를 호출한 노드의 마지막 자식으로 추가한다.

노드 이동

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li>Apple</li>
      <li>Banana</li>
      <li>Orange</li>
    </ul>
  </body>
  <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>
</html>
  • DOM에 이미 존재하는 노드를 appendChild 또는 insertBefore 메서드를 사용하면 DOM에 다시 추가하면 현재 위치에서 노드를 제거하고 새로운 위치에 노드를 추가한다. 즉, 노드가 이동한다.

노드 복사

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li>Apple</li>
    </ul>
  </body>
  <script>
    const $fruits = document.getElementById('fruits');
    const $apple = $fruits.firstElementChild;

    // $apple 요소를 얕은 복사하여 사본을 생성. 텍스트 노드가 없는 사본이 생성된다.
    const $shallowClone = $apple.cloneNode();
    // 사본 요소 노드에 텍스트 추가
    $shallowClone.textContent = 'Banana';
    // 사본 요소 노드를 #fruits 요소 노드의 마지막 노드로 추가
    $fruits.appendChild($shallowClone);

    // #fruits 요소를 깊은 복사하여 모든 자손 노드가 포함된 사본을 생성
    const $deepClone = $fruits.cloneNode(true);
    // 사본 요소 노드를 #fruits 요소 노드의 마지막 노드로 추가
    $fruits.appendChild($deepClone);
  </script>
</html>
  • cloneNode 메서드는 노드의 사본을 생성하여 반환한다.
  • 매게변수에 true를 인수로 전달하면 노드를 깊은 복사하여 모든 자손 노드가 포함된 사본을 생성하고,false를 인수로 전달하거나 생략하면 얕은 복사하여 노드 자신만의 사본을 생성한다. 이때 텍스트 노드도 자손 노드이므로 텍스트 노드도 복사되지 않는다.

노드 교체

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li>Apple</li>
    </ul>
  </body>
  <script>
    const $fruits = document.getElementById('fruits');

    // 기존 노드와 교체할 요소 노드를 생성
    const $newChild = document.createElement('li');
    $newChild.textContent = 'Banana';

    // #fruits 요소 노드의 첫 번째 자식 요소 노드를 $newChild 요소 노드로 교체
    $fruits.replaceChild($newChild, $fruits.firstElementChild);
  </script>
</html>
  • repalceChild 메서드는 자신을 호출한 노드의 자식 노드를 다른 노드로 교체한다. 첫 번째 매개변수 newChild에는 교체할 새로운 노드를 인수로 전달하고, 두 번째 매개변수 oldChild에는 이미 존재하는 교체될 노드를 인수로 전달한다.

노드 삭제

<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li>Apple</li>
      <li>Banana</li>
    </ul>
  </body>
  <script>
    const $fruits = document.getElementById('fruits');

    // #fruits 요소 노드의 마지막 요소를 DOM에서 삭제
    $fruits.removeChild($fruits.lastElementChild);
  </script>
</html>
  • removeChild 메서드는 child 매개변수에 인수로 전달한 노드를 DOM에서 삭제한다.

어트리뷰트

<input id="user" type="text" value="ungmo2">

HTML 문서의 구성 요소인 HTML 요소는 여러 개의 어트리뷰트(속성)을 가질 수 있다. HTML 요소의 동작을
제어하기 위한 추가적인 정보를 제공하는 HTML 어트리뷰트는 HTML 요소의 시작 태그에 어트리뷰트 이름 = 어티르뷰트 값 형식으로 정의한다.

HTML 어트리뷰트 조작

<!DOCTYPE html>
<html>
<body>
  <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
  </script>
</body>
</html>
  • 어트리뷰트 값을 참조할 때 getAttribute 메서드를, 값을 변경할 때 setAttribute 메서드를 사용한다.

0개의 댓글

Powered by GraphCDN, the GraphQL CDN