DOM으로 HTML 조작하기

나는야 토마토·2022년 2월 22일
1

JavaScript💛

목록 보기
3/15
post-thumbnail

DOM이란 무엇인가?

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

그러면 DOM으로 HTML을 어떻게 조작할 수 있을까? 그것에 대한 궁금증에 대해서는 지금부터 알아보쟈!🙂

DOM으로 HTML 조작하기

DOM을 JavaScript로 조작하여 HTML Element를 추가하거나 삭제, 혹은 내용을 변경할 수 있다.
즉, document 객체를 통해서 HTML 엘리먼트를 만들고(CREATE), 조회하고(READ), 갱신하고(UPDATE), 삭제(DELETE) 할 수 있다!
또한 DOM에는 HTML에 적용(APPEND)하는 메소드도 있어서 이 메소드를 통해 조작이 가능하다!

  • createElement - CREATE
  • querySelector, querySelectorAll - READ
  • textContent, id, classList, setAttribute - UPDATE
  • remove, removeChild, innerHTML = "" , textContent = "" - DELETE
  • appendChild - APPEND

CREATE - createElement

DOM으로 HTML을 조작하는 방법 중 가장 기초적인, 새로운 element를 만드는 방법에는 createElement를 이용하는 방법이다!

	document.createElement('div')

위의 Codepen예제를 통해서 새로운 element를 만들어보자!
아래의 사진과 같이 크롬 개발자 도구의 콘솔 탭에서 새로운 div 엘리먼트를 생성해보자!

새롭게 생성한 div element는 어떻게 활용할 수 있나요?

자바스크립트로 활용할 수 있어야한다! 그렇다면 자바스크립트에서 어떠한 결과를 담을 때에는! 변수를 선언하고 작업의 결과를 변수에 할당하면 된다.
여기에서는 div element를 변수 tweetDiv에 할당한다.

const tweetDiv = document.createElement('div')

이렇게 하고 나면 화면에는 아무런 반응이 일어나지 않는다... 그 이유는 tweetDiv 라는 요소는 현재 공중부양 중이기 때문이다!

그림과 같이 createElement 메소드로 생성된 엘리먼트는 공중에 떠있는 상태이다. 이 공중에 떠 있는 엘리먼트를 확인하기 위해서는 APPEND 해야한다!

APPEND - append, appendChild

CREATE에서 생성한 tweetDiv를 append라는 메소드를 사용해서 body에 넣어보자!

document.body.append(tweetDiv)

<div></div>가 생성되서 화면에 보일 줄 알았으나... 아무것도 나오지 않는다.. 그것에 대한 이유는 아무런 내용을 입력하지 않았기에 보이는 내용이 없는 것이다!

크롬의 개발자도구 Elements 탭에서 <div></div>가 생성된 것을 확인 할 수 있다!

그렇다면 내용은 어떻게 넣는 걸까? 궁금증이 생길 것이다. UPDATE에서 textContent를 이용하면 된다!

그 전에 tweetDiv를 #container안에 마지막으로 위치하고 싶다면 어떻게 해야할까? READ의 querySelector를 이용해서 container를 찾고 그 안에 위치시키면 된다!

READ - querySelector, querySelectorAll

자바스크립트에서 원시 자료형인 변수의 값을 조회하기 위해서는 변수의 이름으로 직접 조회할 수 있다. 참조 자료형인 배열은 index를, 객체는 key를 이용해 값을 조회할 수 있다.

여기서 막간을 이용하여! 원시자료형참조 자료형에 대해 이야기해보쟈!

  • 원시자료형은 변경이 불가능한 값을 의미한다.
    즉, 변수가 할당될 때 메모리 고정 크기로 값을 저장하고 해당 주소를 참조하는 특징을 가지고 있다. 그래서 불변성의 특징을 가지고 있다!
  • 원시자료형 6가지) string, number, boolean, undefined, null, symbol
  • ex) let a = 10 ➡ a = 2 로 변경했을 때 새로운 메모리가 생성되고, 그것을 참조하는 주소만 변경되어진다!

  • 참조자료형은 변경이 가능한 값을 의미한다.
  • 대표적인 예시) 객체, 배열, 함수
  • ex) let a = [1,2,3,4] 의 배열을 만들고,
    let b = a b에 a 배열을 선언한 뒤
    b[2] = 2 b에 대한 데이터를 바꾸면 a의 배열 값도 바뀌게 된다.

그러나 DOM은 위에 말했던 원시자료형과 참조자료형을 조회하는 방법과 달리 조금 특별한 방법을 사용해야한다!
DOM으로 HTML 엘리먼트의 정보를 조회하기 위해서는 querySelector의 첫 번째 인자로 셀렉터(selector)를 전달하여 확인할 수 있다. 셀렉터로는 HTML 태그 ("div"), id("#tweetList"), class(.tweet) 이 세가지가 가장 많이 사용된다.

더 자세히 querySelect에 대해 알아보쟈!

querySelect

앞서 예시를 바탕으로 설명하자면,

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

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

하지만 HTML 문서에는 클래스 이름이 tweet인 엘리먼트가 여러 개 있는데, 변수 oneTweet에 할당된 엘리먼트는 단 하나이다! 여러 개의 엘리먼트를 한 번에 가져오기 위해서는, querySelectorAll을 사용해야한다. 이렇게 조회한 HTML 엘리먼트들은 배열처럼 for문을 사용할 수 있다!

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

❗ 하지만...! 앞서 querySelectorAll로 조회한 HTML엘리먼트들은 배열이 아니다! 즉, 배열이 아닌 배열, 유사배열 또는 배열형 객체(Array-like Object)이다!


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

querySelector와 비슷한 DOM 조회 메소드에는 get으로 시작하는 getElementById가 있다. 이러한 메소드는 querySelector 와 비슷한 역할을 하는 오래된 방식이라고 기억해두면 좋다!

이제 다시! 문제로 돌아가서 CREATE에서 생성한 div 엘리먼트container의 맨 마지막 자식 엘리먼트로 추가하는 코드를 작성해보자!

const container = document.querySelector('#container')
const tweetDiv = document.createElement('div')
container.append(tweetDiv)

이제 아래의 그림과 같이 container에 잘 연결되는 것을 확인할 수 있다!

하지만 새롭게 추가된 tweetDiv는 별도의 class 가 지정되어 있지 않아, CSS를 이용한 스타일링이 적용되지 않는다. 이제 tweetDiv에 class를 붙여보자!

UPDATE - textContent, classList.add

우선 그 전에 비어있는 div 엘리먼트에 textContent를 사용해서 문자열을 입력해보자.

console.log(tweetDiv) // <div></div>
tweetDiv.textContent = 'dev';
console.log(tweetDiv) // <div>dev</div>

이제 CSS 스타일링이 적용될 수 있도록, div 엘리먼트에 class를 추가해보자.

tweetDiv.classList.add('tweet')
console.log(tweetDiv) // <div class="tweet">dev</div>

이제 성공적으로 추가된 것을 확인할 수 있다!

DELETE - remove, removeChild

이제 CRUD의 Delete 즉, 삭제하는 방법에 대해서 알아보자.

const container = document.querySelector('#container')
const tweetDiv = document.createElement('div')
container.append(tweetDiv)
tweetDiv.remove() // 이렇게 append 했던 엘리먼트를 삭제할 수 있다.

만약 container에 있는 모든 내용을 삭제하려면 어떻게 해야할 까?
innerHTML 을 이용하면, 아주 간단하게 모든 자식 엘리먼트를 지울 수 있다!

document.querySelector('#container').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를 할당해뒀다가 반복문이 끝난 뒤에 새롭게 추가하기
  • 자식 엘리먼트 하나만 남기기

container의 자식 엘리먼트가 1개만 남을 때까지, 마지막 자식 엘리먼트를 제거하는 방법

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()
}

이 외에도 삭제하는 방법에는 여러가지가 있다!

더 알아보기📚

1) querySelector의 첫번째 인자에 'div'를 넣으면 어떻게 될까?

제일 처음으로 검색되는 div를 뱉거나, 만약 div가 없으면 null 을 리턴한다!

2) querySelector를 통해서 더 복잡한 작업을 할 수 있을까?

할 수 있다! querySelector 는 파워풀해서 복잡한 것도 찾기 때문이다.

3) querySelector의 부모는 꼭 document 객체여야만 할까?

아니다! 어떤 객체든 자식 엘리먼트를 가지고 있다면 querySelector의 부모가 될 수 있기 때문이다.

4) element와 node의 차이 (difference between element and node in javascript dom)

Node는 태그 노드와 텍스트 노드 전체를 가리키고, Element는 텍스트 노드를 제외하고, <a> 태그처럼 태그만 가리킨다. 그래서 태그만 검색하고 싶을 때에는 Element가 붙은 메소드를 선택해야한다. 즉, NodeElement보다 더 큰 범위라고 말한다.

5) children과 childNodes의 차이 (difference between children and childNodes in javascript dom)

ChildNodes는 현재 요소의 자식 노드가 포함된 NodeList를 반환하여 태그노드와 텍스트 노드 모두 다 반환이되고, Children은 HTMLCollection을 반환하고, 비요소는 모두 제외된다.
예시 링크

6) removeChild와 remove의 차이 (difference between removeChild and remove in javascript dom)

remove는 노드를 메모리에서 삭제하고 종료한다. 하지만 removechild는 노드를 삭제하는 것이 아니라 메모리에 해당 노드는 존재하며, 부모-자식관계를 끊어서 DOM 트리에서 해제하는 것이다. 최종적으로는 관계를 끊은 해당 노드의 참조를 반환한다.

7) tweets에 forEach는 되는데, reduce는 안되는 이유 (why array method is not working on nodelist)

(노드리스트에서 배열 방법이 작동하지 않는 이유)

노드리스트는 배열이 아니기 때문입니다. 여기서 forEach는 NodeList의 메소드이기도 해서 사용이 가능하지만, reduce는 배열이 아닌 경우에는 사용이 불가능합니다.

8) tweets를 유사 배열에서 배열로 바꾸는 방법 (how to convert nodelist into javascript array)

  • Array.from() Method
// create a `NodeList` object
const tweets = document.querySelectorAll('.tweet');
// convert `NodeList` to an array
const tweetsArr = Array.from(tweets);
  • Spread Operator - [...iterable]
// create a `NodeList` object
const tweets = document.querySelectorAll('.tweet');
// convert `NodeList` to an array
const tweetsArr = [...tweets];
  • Array.prototype.slice() Method
// create a `NodeList` object
const tweets = document.querySelectorAll('.tweet');
// convert `NodeList` to an array
const tweetsArr = Array.prototype.slice.call(tweets);
// or
const tweetsArr = [].slice.call(tweets);
profile
토마토마토

0개의 댓글