본문 바로가기
TIL/Code States

Code States 15일차 - DOM

by 죠르띠에 2021. 8. 6.

DOM은 Document Object Model의 약자로, HTML 요소를 Object(JavaScript Object)처럼 조작(Manipulation)할 수 있는 Model이다. 즉, 여러분이 자바스크립트를 사용할 수 있으면, DOM으로 HTML을 조작할 수 있다.

 

HTML에 JavaScript 적용하기

HTML에 JavaScript를 적용하기 위해서는 <script> 태그를 이용한다. 아래의 경우 HTML 파일과 같은 디렉토리에 존재하는 myScriptFile.js을 불러온다.

<script src="myScriptFile.js"></script>

웹 브라우저가 작성된 코드를 해석하는 과정에서 <script> 요소를 만나면, 웹 브라우저는 HTML 해석을 잠시 멈춘다. HTML 해석을 잠시 멈춘 웹 브라우저는 <script> 요소를 먼저 실행한다.

 

HTML은 프로그래밍을 위해서 만들어진 언어가 아니기 때문에 이전에 배웠던 조건문이나 반복문을 사용할 수 없고, 정보를 저장하기에도 적합한 언어가 아니다. 그래서 자바스크립트라는 프로그래밍 언어와 DOM을 활용하여 HTML에 접근하고 조작다.

 

자식 엘리먼트 찾기

자바스크립트에서 DOM은 document 객체에 구현되어 있다. 브라우저에서 작동되는 자바스크립트 코드에서는, 어디에서나 document 객체를 조회할 수 있다.

 

DOM 구조를 조회할 때에는 console.dir 이 유용하다. console.dirconsole.log 와 달리 DOM을 객체의 모습으로 출력한다.

 

console.dir(document.body) 를 통해 출력된 객체에서, children 속성을 찾을 수 있다. children 속성에 nav, news-contents, footer 가 자식으로 있는 것을 확인할 수 있다. 물론 document.body.children 으로 바로 조회할 수도 있다.

 

부모 엘리먼트 찾기

Element.closest()

기준 Element 에서부터 closest() 메소드를 통해 자신부터 부모 요소 단위로 출발하여 각 요소가 지정한 선택자에 만족할 때까지 탐색한다(문서 루트까지 이동). 이 중 가장 가깝게 조건에 만족한 부모 요소가 반환되며, 조건에 만족한 요소가 없으면 null 값을 반환한다.

var closestElement = targetElement.closest(selectors);

Node.parentNode

Node.parentNode 읽기 전용 속성은 DOM 트리에서 지정된 노드의 부모를 반환합니다.

parentNode = node.parentNode

 

DOM 순회하기

DOM 구조도를 살펴보면, DOM 구조에서도 회사의 조직도와 유사한 모습을 발견할 수 있다. body가 가장 상위에 있고, 아래에 여러 구성요소가 부모-자식 관계를 가지고 있다.  이런 자료 구조를 컴퓨터 공학에서는 트리 구조라고 한다. 트리 구조의 가장 큰 특징은 부모가 자식을 여러 개 가지고, 부모가 하나인 구조가 반복되는 점이다. 즉, 부모가 가진 하나 또는 여러 개의 자식 엘리먼트를 조회하는 코드를 작성한다면, 여러 번 반복해서 실행하는 코드가 필요하다.


DOM으로 HTML 조작하기

Document.createElement()

HTML 문서에서, Document.createElement() 메서드는 지정한 tagName의 HTML 요소를 만들어 반환합니다. tagName을 인식할 수 없으면 HTMLUnknownElement (en-US)를 대신 반환합니다.

let element = document.createElement(tagName[, options]);

Element.append()

Element.append() 메서드는 Element의 마지막 자식 다음에 Node 개체 또는 DOMString 개체 집합을 삽입한다. DOMString 객체는 동등한 Text 노드로 삽입한다.

let div = document.createElement("div")
let p = document.createElement("p")
div.append(p)

Node.appendChild()

Node.appendChild() 메소드는 한 노드를 특정 부모 노드의 자식 노드 리스트 중 마지막 자식으로 붙인다. 만약 주어진 노드가 이미 문서에 존재하는 노드를 참조하고 있다면 appendChild() 메소드는 노드를 현재 위치에서 새로운 위치로 이동시킨다. (문서에 존재하는 노드를 다른 곳으로 붙이기 전에 부모 노드로 부터 지워버릴 필요는 없다.)

이것은 한 노드가 문서상의 두 지점에 동시에 존재할 수 없다는 것을 의미한다. 그래서 만약 노드가 이미 부모를 가지고 있다면 우선 삭제되고 새로운 위치로 이동한다. 

// 새로운 단락 요소를 생성하고 문서에 있는 바디 요소의 끝에 붙입니다.
var p = document.createElement("p");
document.body.appendChild(p);

Element.append()와 Node.appendChild()의 차이점

  • Element.append()를 사용하면 DOMString 개체를 추가할 수도 있지만 Node.appendChild()는 Node 개체만 허용한다.
  • Element.append()는 반환 값이 없는 반면 Node.appendChild()는 추가된 Node 객체를 반환한다.
  • Element.append()는 여러 노드와 문자열을 추가할 수 있는 반면 Node.appendChild()는 하나의 노드만 추가할 수 있다.

querySelector

querySelector 에 '.tweet' 을 첫 번째 인자로 넣으면, 클래스 이름이 tweet 인 HTML 엘리먼트 중 첫 번째 엘리먼트를 조회할 수 있다.

const oneTweet = document.querySelector('.tweet')

HTML 문서에는 클래스 이름이 tweet 인 엘리먼트가 여러 개 있는 데, 변수 oneTweet 에 할당된 엘리먼트는 단 하나이다. 여러 개의 엘리먼트를 한 번에 가져오기 위해서는, querySelectorAll 을 사용한다. 이렇게 조회한 HTML 엘리먼트들은 배열처럼 for문을 사용하실 수 있다. 주의하세요! 앞서 조회한 HTML 엘리먼트들은 배열이 아니다! 이런 '배열 아닌 배열'을 유사 배열, 배열형 객체 등 다양한 이름으로 부릅니다. 정식 명칭은 Array-like Object 입니다. Array-like Object 같이 개념을 설명하는 용어는 영어로도 명확하게 기억해두는 게 좋다.

const tweets = document.querySelectorAll('.tweet')

 

const getOneTweet = document.getElementById('container')
const queryOneTweet = document.querySelector('#container')
console.log(getOneTweet === queryOneTweet) // true

 

textContent, classList.add

console.log(oneDiv) // <div></div>
oneDiv.textContent = 'dev';
console.log(oneDiv) // <div>dev</div>
// textContent를 이용해 문자열을 입력한다.

 

oneDiv.classList.add('tweet')
console.log(oneDiv) // <div class="tweet">dev</div>
// classList.add를 이용해 'tweet' 클래스를 추가한다.

 

remove, removeChild

const container = document.querySelector('#container')
const tweetDiv = document.createElement('div')
container.append(tweetDiv)
tweetDiv.remove() // 이렇게 append 했던 엘리먼트를 삭제할 수 있다.
// id가 container인 엘리먼트 아래에 tweetDiv를 추가하고, remove로 삭제한다.

innerHTML 을 이용하면, 아주 간단하게 모든 자식 엘리먼트를 지울 수 있다.

document.querySelector('#container').innerHTML = '';

innerHTML 을 이용하는 방법은 분명 간편하고 편리한 방식이지만, innerHTML은 보안에서 몇 가지 문제를 가지고 있다. 이 방법을 대신할 다른 메소드를 사용한다. removeChild 는 자식 엘리먼트를 지정해서 삭제하는 메소드이다. 모든 자식 엘리먼트를 삭제하기 위해, 반복문(while, for, etc.)을 활용할 수 있다.

const container = document.querySelector('#container');
while (container.firstChild) {
  container.removeChild(container.firstChild);
}

removeChildwhile 을 이용해 자식 요소를 삭제하면, 제목에 해당하는 H2 "Tweet List"까지 삭제된다. 이를 방지하기 위한 방법은 여러 가지가 있다. 자식 요소가 담고 있는 문자열을 비교해 "Tweet List"만 남기거나, 새로운 변수를 생성하고 Tweet List를 할당해뒀다가 반복문이 끝난 뒤에 새롭게 추가할 수도 있다. 또는 자식 엘리먼트를 하나만 남기게 할 수도 있다.

const container = document.querySelector('#container');
while (container.children.length > 1) {
  container.removeChild(container.lastChild);
}

또는 직접 클래스 이름이 tweet인 엘리먼트만 찾아서 지우는 방법도 있다.

const tweets = document.querySelectorAll('.tweet')
tweets.forEach(function(tweet){
    tweet.remove();
})
// or
for (let tweet of tweets){
    tweet.remove()
}

'TIL > Code States' 카테고리의 다른 글

Code States 18일차 - React 기초  (0) 2021.08.11
Code States 17일차 - 고차함수  (0) 2021.08.10
Code States 14일차 - Spread/Rest 문법  (0) 2021.08.05
Code States 13일차 - 클로저  (0) 2021.08.04
Code States 13일차 - 스코프  (0) 2021.08.04