[JavaScript] DOM? 내가 아는 건 참돔뿐인디?

hyeonbin·2023년 4월 25일
0

JS 계란반 스터디

목록 보기
5/12
post-thumbnail

📃 DOM (Document Object Model)

🪧 DOM 이란?

MDN에 따르면 문서 객체 모델 ( The Document Object Model, 이하 DOM ) 은 HTML, XML 문서의 프로그래밍 인터페이스다.

  • 텍스트 파일로 만들어져 있는 웹 문서를 브라우저에 렌더링하려면 웹 문서를 브라우저가 이해할 수 있는 구조로 메모리에 올려야 하는데, 브라우저의 렌더링 엔진은 웹 문서를 로드한 후, 파싱해 웹 문서를 브라우저가 이해할 수 있는 구조로 구성해 메모리에 적재하는데 이를 DOM이라고 한다.

  • 즉, HTML 문서의 내용을 각각의 객체로 만들고 이들 객체를 부자관계로 표현 할 수 있는 트리형태로 구조화해 웹페이지와 프로그래밍 언어를 연결시켜주는 역할을 하고, 이때 각각의 요소와 속성, 콘텐츠를 표현하는 단위를 노드 ( node )라고 한다.


DOM의 2가지 기능

1. HTML 문서에 대한 모델 구성

  • 브라우저는 HTML 문서를 로드한 후, 해당 문서에 대한 모델을 메모리에 생성한다.
    이때 모델은 객체의 트리로 구성되는데, 이것을 DOM tree라 한다.

2. HTML 문서 내의 각 요소에 접근 / 수정

  • DOM은 모델 내의 각 객체에 접근하고 수정할 수 있는 프로퍼티와 메소드를 제공하는데, 이를 DOM API라고 부른다. DOM이 수정되면 브라우저를 통해 사용자가 보게 될 내용 또한 변경된다.


🪧 DOM tree

  • 객체의 트리로 구조화되어 있기에 DOM tree라고 부른다.
  • DOM tree는 브라우저가 HTML 문서를 로드한 후 파싱해 생성하는 모델을 의미한다.
  • DOM tree의 진입점은 Document 객체이며, 최종점은 요소의 text를 나타내는 객체다.
  • DOM tree는 4종류의 노드로 구성된다.



🪧 DOM tree 접근하기

  • document 객체를 통해 HTML 문서에 접근할 수 있다.
  • document는 브라우저가 불러온 웹페이지를 나타내며, DOM 트리의 진입점 역할을 수행한다.
  // 해당하는 Id를 가진 요소 노드 하나에 접근
  // 복수개가 선택된 경우, 첫번째 요소만 반환
  document.getElementById('id');

  // 태그명으로 해당하는 모든 요소 노드에 접근
  document.getElementsByTagName('tagName');

  // 해당하는 클래스를 가진 모든 요소 노드에 접근
  // 공백으로 구분해 여러 개의 class를 지정
  document.getElementsByClassName('class');

  // css 선택자로 단일 요소 노드에 접근
  // 복수개가 선택된 경우, 첫번째 요소만 반환
  document.querySelector('Selector');

  // css 선택자로 여러 요소 노드에 접근
  document.querySelectorAll('Selector');


🪧 DOM 제어 명령어

1. 이벤트 삽입

<button>DOM! DOM!</button>
const btn = document.querySelector('button');

// target.addEventListener( type, listener ) 문법 형태
// 이벤트의 타입에는 click, mouseover, mouseout, wheel 등 다양한 이벤트를 감지
// listener 함수의 인수에는 이벤트에 대한 정보가 담김
btn.addEventListener('click', function() {
  console.log('DOM World~!');
})


2. 클래스 제어

<button>I Love green!</button>
const btn = document.querySelector('button');

btn.addEventListener('click', function(){

// DOM api를 통해 요소의 class 속성을 제어
// green 라는 클래스의 속성 값 지정 가능
btn.classList.add('green');

// btn.classList.remove('green');   클래스를 제거
// btn.classList.toggle('green');   클래스를 토글합니다. 없으면 넣어주고, 있으면 제거
// btn.classList.contains('green'); 해당하는 클래스가 있는지 확인
})


3. 요소 제어

<ul></ul>
<button>Make me MORE!</button>
// document.createElement(target);  target 요소를 생성
// document.createTextNode(target); target 텍스트를 생성
// element.appendChild(target);     target 요소를 element의 자식으로 위치
// element.removeChild(target);     element의 target 자식 요소를 제거

const btn = document.querySelector("button");
const ul = document.querySelector("ul");

btn.addEventListener('click', function(){
	for(let i=0; i < 5; i++){
		const li = document.createElement('li');
		ul.appendChild(li);
	}
})

<div id="parentElement">
    <span id="childElement">hello green</span>
</div>
// parentElement.insertBefore(target, location); 
// target요소를 parentElement의 자식인 location 위치를 앞으로 이동

const span = document.createElement("span");
const sibling = document.getElementById("childElement");
const parentDiv = document.getElementById("parentElement");
parentDiv.insertBefore(span, sibling);


4. JavaScript 문자열을 사용해 element, text 노드를 생성하거나 추가하기

<p></p>
<input type="text">
<button>Write Something!</button>
const btn = document.querySelector('button');
const p = document.querySelector('p');
const input = document.querySelector('input');

btn.addEventListener('click', function () {
  p.textContent = input.value;
});

// input 요소에 'input' 이벤트를 연결하면 실시간으로 값이 반영되게 만들 수 있음
input.addEventListener('input', () => {
  p.textContent = input.value;
});

p.innerHTML = '<strong>나는 강하다 무하하</strong>';

// innerHTML은 요소(element) 내에 포함된 HTML 마크업을 가져오거나 설정
// innerText 속성은 요소의 렌더링 텍스트 콘텐츠를 나타냄 (렌더링됨에 주목, innerText는 "사람이 읽을 수 있는" 요소만 처리)
// textContent 속성은 노드의 텍스트 콘텐츠를 표현
// innerText, textContent 차이점 주의!!


5. 더 인접한 곳으로 정밀하게 배치하기

<strong class="sayHi"> 하이하이! </strong>
// insertAdjacentHTML : 요소 노드를 주어진 위치에 배치
const sayHi = document.querySelector('.sayHi');
sayHi.insertAdjacentHTML('beforebegin', '<span>안녕하세요! 제 이름은</span><br>');
sayHi.insertAdjacentHTML('afterbegin', '<span>주노입니다.</span><br>');
sayHi.insertAdjacentHTML('beforeend', '<span>추노아닙니다.</span><br>');
sayHi.insertAdjacentHTML('afterend', '<span>주우~ 노우~ 오케이~?!</span><br>');


6. DOM 안에서 노드 탐색하기

<article class="cont">
  <h1>안녕하세요. <br />저는 주니어 개발자입니다.</h1>
  <p>지금부터 자기소개를 해보겠습니다.</p>
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Deserunt incidunt voluptates
    laudantium fugit, omnis dolore itaque esse exercitationem quam culpa praesentium, quisquam
    repudiandae aut. Molestias qui quas ea iure officiis.
  </p>
  <strong>감사합니다!</strong>
</article>
const cont = document.querySelector('.cont');
// 첫번째 자식을 찾음 - <h1>
console.log(cont.firstElementChild);
// 마지막 자식을 찾음 - <strong>
console.log(cont.lastElementChild);
// 다음 형제요소를 찾음 - 없으니까 null 반환
console.log(cont.nextElementSibling);
// 이전 형제노드를 찾음 - <p>
console.log(cont.previousSibling);
// 모든 직계자식을 찾음 - 모든 직계 자식 요소 <h1>, <p>, <strong>
console.log(cont.children);
// 부모 요소를 찾음 - 없으니까 null 반환
console.log(cont.parentElement);



🪧 이벤트 객체

  • 브라우저 화면에서 이벤트가 발생하면 브라우저는 가장 먼저 이벤트 대상을 찾는다.

  • 캡처링 단계는 가장 상위의 window 객체부터 document, body 순으로 DOM 트리를 따라 내려간다.

  • 버블링 단계는 브라우저는 중간에 만나는 모든 캡처링 이벤트 리스너를 실행시킨다. 그리고 이벤트 대상을 찾고 캡처링이 끝나면 다시 DOM 트리를 따라 올라가며 만나는 모든 버블링 이벤트 리스너를 실행한다.

  • 이벤트 전파는 이런 과정에서 이벤트 리스너가 차례로 실행되는 것이다.


  <article class="parent">
    <button class="btn" type="button">button</button>
  </article>
  const parent = document.querySelector('.parent');
  const btnFirst = document.querySelector('.btn');
  btnFirst.addEventListener('click', (event) => {
    console.log('btn capture!');
  });
  window.addEventListener(
    'click',
    () => {
      console.log('window capture!');
    },
    true
  ); // true : 캡처링 단계의 이벤트가 발생하도록 함
  document.addEventListener(
    'click',
    () => {
      console.log('document capture!');
    },
    true
  );
  parent.addEventListener(
    'click',
    () => {
      console.log('parent capture!');
    },
    true
  );
  btnFirst.addEventListener('click', (event) => {
    console.log('btn bubble!');
  });
  parent.addEventListener('click', () => {
    console.log('parent bubble!');
  });
  document.addEventListener('click', () => {
    console.log('document bubble!');
  });
  window.addEventListener('click', () => {
    console.log('window bubble!');
  });



🪧 이벤트 target, currentTarget

  • 부모부터 자식까지 일련의 요소를 모두 타고가며 진행되는 이런 이벤트의 특징 덕분에 이벤트 객체에는 target, currentTarget 이라는 속성이 존재한다.

  • target의 속성에는 이벤트가 발생한 곳의 정보가 담겨 있다. 이 속성을 통해 이벤트 리스너가 없는 요소의 이벤트가 발생했을 때도 해당 요소에 접근 가능하다.

  • currentTarget의 속성에는 이벤트 리스너가 연결돼 요소가 참조되고 있다.

  <article class="parent">
    <ol>
      <li><button class="btn-first" type="button">button 1</button></li>
      <li><button type="button">button 2</button></li>
      <li><button type="button">button 3</button></li>
    </ol>
  </article>
  const parent = document.querySelector('.parent');
  parent.addEventListener('click', function (event) {
    console.log(event.target);
    console.log(event.currentTarget);
  });



🪧 이벤트 위임

  • 이벤트 위임은 이벤트가 리스너가 없는 요소의 이벤트가 발생했을 때도 해당 요소에 접근해서 리스너가 있는 것처럼 사용할 수 있다.

  <article class="parent">
    <ol>
      <li><button class="btn-first" type="button">button 1</button></li>
      <li><button type="button">button 2</button></li>
      <li><button type="button">button 3</button></li>
    </ol>
  </article>
  const parent = document.querySelector('.parent');
  parent.addEventListener('click', function (event) {
    console.log(event.target);
    if (event.target.nodeName === "BUTTON") {
      event.target.innerText = "button 4";
    }
  })



🪧 이벤트의 this

  • 이벤트 리스너 함수 내부에서의 this 값은 이벤트가 연결된 노드를 참조한다.

  • event.currentTarget 속성의 참조값과 유사하다.

  • 이벤트 리스너 함수를 화살표 함수로 쓴다면 this가 가리키는 대상이 달라지는 점을 유의하자!
    ※ 화살표 함수의 this는 자신을 둘러싸고 있는 외부 환경의 this 값을 참조한다.

  <article class="parent">
    <ol>
      <li><button class="btn-first" type="button">button 1</button></li>
      <li><button type="button">button 2</button></li>
      <li><button type="button">button 3</button></li>
    </ol>
  </article>
  const parent = document.querySelector('.parent');
  parent.addEventListener('click', function (event) {
    console.log(this);
  })



🪧 preventDefault( )

  • preventDefault ( ) 는 브라우저의 기본 이벤트 동작을 취소한다. 브라우저는 HTML 태그를 통해 여러가지 기능들을 제공하지만, 가끔 그러한 기능이 방해가 되는 경우가 있다.

  • 아래의 예제처럼 종종 브라우저의 기본 동작을 중지하고, 자바스크립트를 통해 기능을 처리하고자 할 때 사용한다.

  <!-- 앵커의 기본 동작을 중지 -->
  <a href="https://www.naver.com" class="link">네이버 링크입니다아아아아만...</a>
  <script>
      const link = document.querySelector('.link');
      link.addEventListener('click', (event) => {
          console.log('clicked');
          event.preventDefault();
      })
  </script>

  <!-- submit의 기본 동작을 중지 -->
  <form action="">
      <button type="submit" class="submit">submit</button>
  </form>
  <script>
      const submit = document.querySelector('.submit');
      submit.addEventListener('click', (event) => {
          console.log('clicked');
          event.preventDefault();
      })
  </script>



🪧 stopPropagation( )

  • 앞에서 우리는 preventDefault ( ) 를 통해 브라우저의 기본 이벤트 동작을 취소해봤는데,
    이벤트 전파 ( 이벤트 프로파게이션 ) 를 막지는 못한다.

  • 이때 이벤트 전파를 막고 싶다면, event.stopPropagation ( ) 를 추가한다.

  <form action="">
      <button type="submit" class="submit">submit</button>
  </form>

  <script>
      const submit = document.querySelector('.submit');
      submit.addEventListener('click', (event) => {
          console.log('clicked');
          // event.preventDefault();
          event.stopPropagation();
      });

      document.body.addEventListener('click', () => {
          console.log('event still alive!');
      });
  </script>

profile
할 수 있다고 믿는 사람은 결국 그렇게 된다 😄😊

0개의 댓글