객체를 사용해서 웹 문서에 체계적으로 접근하고 제어할 수 있는 방법인 DOM을 알아보자!🍀
프론트엔드 개발을 하다 보면, HTML 요소를 숨겨야 한다던가, CSS 스타일을 바꿔야 한다던가.. 뭐 하여튼 동적으로 조작해야 하는 경우가 매우 많다. 이를 위해서 JavaScript에는 문서 객체 모델(DOM, Document Object Model)을 지원한다. 이를 활용하면 HTML 요소를 선택하고, 변경하고, 이벤트를 추가하는 등의 작업을 수행할 수 있다. 오늘은 JavaScript에서 이렇게 고마운 친구인 DOM을 다루는 법을 알아 보도록 하자!
✔️문서 객체 모델(DOM)는 뭐하는 친구이더냐..?
DOM(Document Object Model)은 웹 문서를 JavaScript에서 다룰 수 있도록 트리 구조로 표현한 것이다. 쉽게 말하면, 웹 문서를 구성하고 있는 요소 간의 관계를 체계적으로 표현한 것이라 이해하면 된다. 이러한 구조를 통해서, JavaScript로 HTML 요소를 선택하고, 수정하고, 이벤트를 추가하는 등의 놀라운(?) 작업을 할 수 있다.
🌲DOM 트리
HTML 문서는 계층적인 구조로 이루어져 있으며, DOM 트리는 부모-자식 관계를 형성한다. 그 계층 관계를 표현한 것을 DOM 트리라고 부른다. 예를 들어, 아래와 같은 html 문서가 있다고 하면..
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html><html><head><title>참돔, 감성돔, 그저 DOM...</title></head><body><h1id="title">집에 가고 싶어요</h1><pclass="description">JavaScript 재미 있어질 수 있을까요?</p></body></html>
위의 html 문서를 DOM 트리로 표현하면 다음과 같다고 볼 수 있다.
1
2
3
4
5
6
html
├── head
│ ├── title ("참돔, 감성돔, 그저 DOM...")
├── body
│ ├── h1#title ("집에 가고 싶어요")
│ ├── p.description ("JavaScript 재미 있어질 수 있을까요?")
id 속성은 명칭 앞에 #를 붙이고, class 속성은 명칭 앞에 .를 붙여 식별하는 것을 생각하면, 위에 표현되어 있는 계층 구조의 표현법이 이해가 갈 것이다🙂↕️🙂↕️
✔️DOM 요소에 접근하기
웹 문서에서 특정 요소를 선택하는 방법에는 대표적으로 2가지가 있다. 지금부터 그 방법들을 하나씩 알아볼까?
1. getElement-* 메서드 사용하기
JavaScript에서 특정 요소를 선택하는 가장 기본적인 방법이다. getElementById, getElementByClassName, getElementsByTagName 메서드를 제공한다. 아래의 간단한 예시들을 살펴보자.
1
2
3
lettitle=document.getElementById("title");// ID로 요소 선택하기!letdescriptions=document.getElementsByClassName("description");// 클래스명으로 요소 선택하기!letparagraphs=document.getElementsByTagName("p");// 태그명(p, h1 등)으로 요소 선택하기!
참고로 document 객체는 웹 페이지 그 자체를 의미한다. (쉽게 말하면.. 웹 문서의 최상단 부모 객체라고 생각하면 된다)
2. querySelector()와 querySelectorAll() 사용하기
위의 메소드들은 상당히 길다. 어느 세월에 getElementsByTagName 어쩌구를 치고 앉아있을 테인가… 다행히 더 간편한 방법이 준비되어 있다! 바로 querySelector()와 querySelectorAll()!!
1
2
3
4
5
lettitle=document.querySelector("#title");//ID 선택letdescriptions=document.querySelector(".description");//클래스 선택letallDescriptions=document.querySelectorAll(".description");//여러 개 선택(NodeList를 반환)letimage=document.querySelector("img");//<img> 태그 선택 (여러 개일 경우 첫 번째 <img> 요소만 반환)letimages=document.querySelectorAll("img");//<img> 태그 선택 (모든 <img> 요소들 NodeList로 반환)
querySelector()는 해당하는 첫 번째 요소만 반환하고, querySelectorAll()은 모든 요소를 반환한다. 이 차이점을 잘 기억해 두자!
크롬 개발자 도구로 확인한 위의 사진을 보면 알 수 있듯이, 같은 매개변수에 관하여 querySelector()는 해당하는 첫 번째 요소만 반환하고, querySelectorAll()은 관련한 모든 요소를 반환한다.
✔️DOM 요소 내용 변경하기
선택한 요소에 관해서 텍스트 내용에 접근하고 수정할 수 있다. 접근하는 3가지 옵션이 있는데, 이들은 미묘한 차이가 있다.
innerText: 요소의 텍스트에 대해서 접근
innerHTML: HTML 태그를 포함하여 접근
textContent: HTML 태그를 무시하고 순수 텍스트에만 접근
이렇게만 들으면 진짜 뭐라는지 하나도 모르겠다, 짚고 넘어갈 필요가 있다! 아래의 예제를 살펴보자.
1
2
3
4
<divid="contents">
안녕 친구들?
<spanstyle="display: none">난 숨겨져 있는데 누가 볼 수 있을까?</span></div>
위와 같은 간단한 html 문서에 대해서, id가 contents인 div에 다양한 방식으로 접근한 결과를 크롬 개발자 도구를 이용해서 확인해 보면 아래와 같다.
innerText는 말 그대로, 요소 내부의 사용자에게 '보이는' 텍스트(display: none인 글자엔 접근하지 않으니까!)에만 접근하고, innerHTML은 html 태그를 포함한 내부의 모든 요소에 접근하고, textContent는 html 태그를 제외한 모든 텍스트에 접근하는 것을 확인 가능하다!
✔️DOM에서 이벤트 처리하기
웹 페이지에서 사용자와 상호작용하려면 클릭, 마우스 커서 갖다 대기 등의 다양한 이벤트를 처리해야 한다. 이와 관련된 내용에 대해서도 한 가지씩 알아보자.
1. 이벤트 연결 방법
이벤트를 DOM 요소에 연결하는 방법엔 우선, DOM 요소에 함수를 직접 연결하는 방법이 있다.
새로운 요소를 동적으로 생성할 때는 createElement() 와 appendChild()를 사용한다. (새로운 텍스트 노드를 생성하기 위해 createTextNode()를 사용하기도 한다)
1
2
3
letnew_Paragraph=document.createElement("p");//새로운 요소 <p> 생성new_Paragraph.innerText="집가서 누워 있고 싶어요";//new_Paragraph 요소의 텍스트 내용 변경document.body.appendChild(new_Paragraph);//body의 child(자식) 요소로 new_Paragraph 추가
📌innerText vs createTextNode() 두 방법 모두 요소의 텍스트를 변경하는 데 사용할 수 있지만, 동작 방식이 다른 것을 알아 두어야 한다! innerText는 기존 요소의 텍스트를 변경하는 데 사용 되지만, createTextNode()는 새로운 텍스트 노드를 생성해서 문서에 추가하는 데 사용된다. 정리하면, innerText는 기존의 html 구조를 유지 하면서 내용을 바꾸는 반면, createTextNode()는 독립적인 텍스트 노드를 추가할 때 유용하게 사용된다!
개발자 도구로 살펴 봤을 때, createTextNode()는 기존의 html 구조가 변하며, 아예 독립적인 노드가 생기는 것을 확인 가능하다.
2. 요소 삭제하기
새로운 요소를 동적으로 삭제할 때는 remove()를 활용한다
1
new_Paragraph.remove();//요소 삭제
3. 클래스 속성 추가 및 삭제하기
특정 요소의 class 속성을 추가 하거나, 삭제 하고 싶을 때도 있을 것이다. 그런 경우엔 classList의 add(), remove(), toggle() 메소드를 활용하면 유용할 것이다!
1
2
3
title.classList.add("highlight");// title 요소에 'highlight' class 속성 추가title.classList.remove("highlight");// title 요소에 'highlight' class 속성 추가title.classList.toggle("active");// title 요소에 대한 class 토글 ('active' class 속성을 추가(add)/제거(remove)를 번갈아 가며 수행)
지금까지 JavaScript에서 DOM을 다루는 기본적인 방법들을 알아 보았다. 아래의 예시를 살펴 보며 DOM을 어떻게 사용하는지 복습하며 마무리해 보도록 하자. 각 코드에 대한 설명은 주석으로 상세히 기록해 놓았다.
//이와 관련된 html, CSS 코드는 분량 상 생략함document.getElementById('modeSwitcher').addEventListener('click',()=>{constbodyElement=document.body;constmodeSwitcher=document.getElementById('modeSwitcher');// body 클래스를 토글해서 라이트 모드와 다크 모드 전환bodyElement.classList.toggle('dark-mode');//'dark-mode'가 class 속성으로 들어가 있으면 제거, 없으면 추가bodyElement.classList.toggle('light-mode');//'light-mode'가 class 속성으로 들어가 있으면 제거, 없으면 추가// 버튼 텍스트 업데이트if (bodyElement.classList.contains('dark-mode')){//bodyElement 요소의 class 속성에 'dark-mode'가 포함되어 있다면modeSwitcher.textContent='라이트 모드';//modeSwitcher 요소의 텍스트를 '라이트 모드'로 변경}else{modeSwitcher.textContent='다크 모드';//modeSwitcher 요소의 텍스트를 '다크 모드'로 변경}// 삼항 연산자// bodyElement.classList.contains('dark-mode') ?// modeSwitcher.textContent = '라이트 모드' :// modeSwitcher.textContent = '다크 모드';});
위의 코드를 통해 아래와 같이 동적으로 라이트/다크 모드를 토글할 수 있는 간단한 UI를 완성할 수 있었다.
DOM을 다루는 법을 익혀 멋진 홈페이지를 만드는 데에 기여해 보자!!
🤪정리
오늘 열심히 알아봤던 내용은 아래와 같다.
DOM 트리와 요소 접근 방법
HTML 요소의 내용 변경 방법
이벤트 처리 및 요소 추가/삭제 방법
실제 프로젝트에서 DOM을 조작하는 다양한 상황이 발생할 것이다. 라이트/다크 모드는 물론이고, 팝업 on/off 등등 생각해보면.. 사용할 만한 곳이 정말 많지 않은가?! 나도 수업 시간에 배우면서 헷갈려서 수업 중엔 실습을 제대로 완성하지 못했다…🙂↕️ 차분히 html 문서 내부의 요소들에 접근하는 다양한 방법들을 복습하고 넘어가도록 하자!