DOM

피자냠냠(피자냠냠)·2023년 1월 5일
0
post-thumbnail

DOM의 기초

DOM이란 무엇인가?

웹브라우저는 HTML 문서를 해석하고, 화면을 통해 해석된 결과를 보여준다. 해석한 HTML를 화면을 통해 보여주는 과정을 렌더링이라 한다.

브라우저는 HTML코드를 해석해서 요소들을 트리 형태로 구조화해 표현하는 문서(데이터)를 생성한다. 이를 DOM(Document Object Model)이라 한다.

브라우저는 DOM을 통해 화면에 웹 콘텐츠들을 렌더링한다.

DOM은 자바스크립트를 사용해서 웹 화면의 콘텐츠를 추가,수정,삭제하거나 이벤트를 처리할 수 있도록 프로그래밍 인터페이스를 제공한다.

DOM의 적용

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

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

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

<script> 요소를 추가하는 두 가지 대표적인 사례가 존재합니다.

  • <head> 요소에 추가하는 방법
  • </body> 가 끝나기 전에 추가하는 방법입니다.

<head>에 있으면 그 지점에서 html 해석을 멈춤으로 <body>에 있는 id나 class를 제대로 끌고 올 수가 없고

<body>에 모든 HTML의 해석이 끝난뒤에 실행됨으로 문제가 안 생긴다.

DOM 트리

HTML을 지탱하는 것은 태그(tag)입니다.

문서 객체 모델(DOM)에 따르면, 모든 HTML 태그객체입니다. 태그 하나가 감싸고 있는 ‘자식’ 태그는 중첩 태그(nested tag)라고 부릅니다. 태그 내의 문자(text) 역시 객체입니다.

이런 모든 객체는 자바스크립트를 통해 접근할 수 있고, 페이지를 조작할 때 이 객체를 사용합니다.

document.body<body> 태그를 객체로 나타낸 것이죠.

HTML에서 객체의 정의란??

DOM 트리 예제

<!DOCTYPE HTML>
<html>
<head>
  <title>사슴에 관하여</title>
</head>
<body>
  사슴에 관한 진실.
</body>
</html>

DOM은 HTML을 태그 트리 구조로 표현한다.

트리에 있는 노드(node)는 모두 객체이다.

태그는 요소 노드(element node)(혹은 그냥 요소)이고, 트리 구조를 구성한다.

<html>은 루트 노드가 되고,
<head>와 <body>는 루트 노드의 자식이 된다.

요소 내의 문자는 텍스트(text) 노드가 된다. 위 그림에서 #text를 확인해봐라. 텍스트 노드는 문자열만 담는다. 자식 노드를 가질 수 없고, 트리의 끝에서 잎 노드(leaf node)가 된다.

자동교정
기형적인 HTML을 만나면 브라우저는 DOM 생성과정에서 HTML을 자동으로 교정한다.

<p>안녕하세요
<li>엄마
<li>그리고
<li>아빠

이렇게 닫는 태그가 없어도, 자동으로 빠진 부분을 채워넣어준다.

노드 타입

노드 타입은 총 열두 가지 인데, 실무에선 주로 다음 네 가지 노드를 다룬다.

  • DOM의 '진입점"이 되는 문서 노드(document node)
  • HTML 태그에서 만들어지며, DOM 트리를 구성하는 블록인 요소 노드(element node) ★★★
  • 텍스트를 포함하는 텍스트 노드(text node)
  • 화면에 보이지는 않지만, 정보를 기록하고 자바스크립트를 사용해 이 정보를 DOM으로부터 읽을 수 있는 주석 노드(comment node)

주석(comment) 노드
js에서 주석처리를 해도 DOM에 나타난다. 이를 주석노드라고 한다.
물론 화면에는 나타나지 않는다.

DOM 구조 직접보기

Live DOM Viewer 에 들어가면 실시간으로 DOM구조를 볼 수 있다.

또는 개발자도구를 통해서 DOM구조를 확인 할 수 있다.

DOM 탐색하기


DOM에 수행하는 모든 연산은 document 객체에서 시작합니다. document 객체는 DOM에 접근하기 위한 '진입점’이죠. 진입점을 통과하면 어떤 노드에도 접근할 수 있습니다.

아래 그림은 DOM 노드 탐색이 어떤 관계를 통해 이루어지는지를 보여줍니다.

  • console.dir()
    document 객체를 조회할 수 있다.

트리상단 탐색하기

  • <html> = document.documentElement
    document를 제외하고 DOM 트리 꼭대기에 있는 문서 노드는 <html> 태그에 해당하는 document.documentElement입니다.

  • <body> = document.body
    document.body는 <body> 요소에 해당하는 DOM 노드로, 자주 쓰이는 노드 중 하나입니다.

  • <head> = document.head
    <head> 태그는 document.head로 접근할 수 있습니다.

자식 노드 탐색하기

  • childNodes 컬렉션은 텍스트 노드를 포함한 모든 자식 노드를 담고 있습니다.

childNodes 주의사항 (Array-like Object)
childNodes는 마치 배열 같아 보입니다. 하지만 childNodes는 배열이 아닌 반복 가능한(iterable, 이터러블) 유사 배열 객체인 컬렉션(collection) 입니다.
특징
1. for..of를 사용할 수 있습니다.
2. 배열이 아니기 때문에 배열 메서드를 쓸 수 없습니다.
만약 진짜 배열로 만들고 싶다면 Array.from을 사용하면 된다.

  • children 자식노드 중 요소 노드만 담고있다.

  • firstChildlastChild 프로퍼티를 이용하면 첫 번째, 마지막 자식 노드에 빠르게 접근할 수 있습니다.

  • firstElementChildlastElementChild 프로퍼티는 각각 첫 번째 자식 요소 노드마지막 자식 요소 노드를 가리킵니다.

elem.hasChildNodes()
자식 노드의 존재 여부를 검사할 때 사용

null
DOM에서 null값은 '존재하지 않음’이나 '해당하는 노드가 없음’을 의미합니다.

형제와 부모 노드

같은 부모를 가진 노드는 형제(sibling) 노드라고 부릅니다.

<head>와 <body>는 대표적인 형제 노드입니다.

<html>
  <head>...</head><body>...</body>
</html>
  • <body>는 <head>의 ‘다음(next)’혹은 '우측(right)'에 있는 형제 노드입니다.
  • <head>는 <body>의 ‘이전(previous)’ 혹은 '좌측(left)'에 있는 형제 노드입니다.
  • parentNode부모 노드를 담아준다.
// <body>의 부모 노드는 <html>입니다
alert( document.body.parentNode === document.documentElement ); // true

// <head>의 다음 형제 노드는 <body>입니다.
alert( document.head.nextSibling ); // HTMLBodyElement

// <body>의 이전 형제 노드는 <head>입니다.
alert( document.body.previousSibling ); // HTMLHeadElement
  • parentNode 프로퍼티는 '종류에 상관없이 부모 노드’를 반환합니다.
  • parentElement부모 요소 노드를 가리킵니다.

  • previousElementSiblingnextElementSibling형제 요소 노드를 가리킵니다.

CRUD(Create, Read, Update and Delete)


Create

  • createElement()
    요소 만드는 프로퍼티
document.createElement('div') // <div></div>
const tweetDiv = document.createElement('div')

변수명을 정하고 createElement 프로퍼티를 실행하면 변수명이 있는 하나의 element를 만들어진다. 이 element는 어디에 속해있지않고 떠다니는중이다.

APPEND

떠다니는 element를 <body>안에 넣으려면 어떻게 해야할까?

  • append()
    자식요소로 만들어준다.

식별자 tweetDiv로 지정된 <div></div>를 body의 자식요소가 되도록 만든다.

const tweetDiv = document.createElement('div')

document.body.append(tweetDiv)

READ

DOM에서 특정한 class, id를 선택하려면 어떻게해야할까?

  • querySelector
    지정한 요소 중 첫 번째 요소를 가져온다.

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

const oneTweet = document.querySelector('.tweet')
  • querySelectorAll
    지정한 요소 모두를 가져온다.

여러 개의 요소를 한 번에 가져오기 위해서는, querySelectorAll 을 사용한다. 이렇게 조회한 HTML 요소들은 배열처럼 for문을 사용하실 수 있습니다. (배열이 아닌것을 기억해두자!! 정식명칭 Array-like Object)

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

querySelectorAll 주의사항 (Array-like Object)
위의 childNodes 주의사항과 같다.
childNodes는 마치 배열 같아 보입니다. 하지만 childNodes는 배열이 아닌 반복 가능한(iterable, 이터러블) 유사 배열 객체인 컬렉션(collection) 입니다.
특징
1. for..of를 사용할 수 있습니다.
2. 배열이 아니기 때문에 배열 메서드를 쓸 수 없습니다.
만약 진짜 배열로 만들고 싶다면 Array.from을 사용하면 된다.

get으로 시작하는 메서드
get으로 시작하는 DOM 조회 메서드를 볼 수도 있다. 이런 메서드는 querySelector 와 비슷한 역할을 하는 오래된 방식이라고만 이해하면 됩니다.

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

CREATE에서 생성한 div 요소를 container에 넣을 준비를 마쳤습니다. 다음 코드를 입력하면, container의 맨 마지막 자식 요소로 tweetDiv를 추가합니다.

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

container.append(tweetDiv)

UPDATE

태그에 class, id를 넣어주거나 문자열을 입력시키기 위해서 어떻게 해야할까?

우선 oneDiv라는 div태그를 만들고 (이 태그는 어디에 속해있지않고 떠다니는 중)

const oneDiv = document.createElement('div');
console.log(oneDiv) // <div></div>

onDiv태그안에 문자열을 입력해보자.

  • textContent 사용
    텍스트값만 지정한다.

마지막 노드라면 이렇게 해도된다.
innerHTML = '텍스트 값'

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

oneDiv에 class를 설정해준다.

  • classList.add()사용
    class를 넣어준다.
oneDiv.classList.add('tweet')
console.log(oneDiv) // <div class="tweet">dev</div>
  • id 생성
    document.querySelector('div').id = 'id이름';
    document.querySelector('div').setAttribute('id', 'id이름')

클래스 지우려면

  • classList.remove()
    class를 지워준다.
  • id 지우기
    document.querySelector('div').removeAttribute('id');

oneDiv를 #container의 자식 요소로 추가한다.
append 사용

const container = document.querySelector('#container')
container.append(oneDiv)

DELETE

지금까지 힘들게 만들어본 태그를 삭제해보자.

  • remove()
    정확한 위치의 하나의 요소를 지워준다.

remove()는 삭제하려는 요소의 위치를 알고 있을 때 사용한다.
변수명을 알고있을때??

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

class인 hide를 지워서 나타나게 할 수 있다.
JS Bin on jsbin.com

여러개를 지우려면 어떻게 해야할까?

  • removeChild()
    자식 요소를 지워준다.

removeChild 는 자식 요소를 지정해서 삭제하는 메서드입니다. 모든 자식 요소를 삭제하기 위해, 반복문(while, for, etc.)을 활용할 수 있습니다.

이 코드는 #container의 자식 요소가 남아있지 않을 때까지, 첫 번째 자식 요소를 삭제하는 코드이다.

const container = document.querySelector('#container');
while (container.firstChild) {
  container.removeChild(container.firstChild);
}
  • innerHTML
    자식요소를 모두 지워준다.
    오류가 생길 수 있어 사용하지 말자.
document.querySelector('#container').innerHTML = '';

removeChild 와 while 을 이용해 자식 요소를 삭제하면, 제목에 해당하는 H2 "Tweet List"까지 삭제됩니다.

이를 방지하기 위한 방법은 여러 가지가 있습니다.
1. 자식 요소가 담고 있는 문자열을 비교해 "Tweet List"만 남긴다.
2. 새로운 변수를 생성하고 Tweet List를 할당해뒀다가 반복문이 끝난 뒤에 새롭게 추가할 수도 있다.
3. 자식 요소를 하나만 남기게 할 수도 있다.

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

유효성 검사

사이트에서 원하는 조건에 맞게 반드시 형식을 맞춰 입력해야 하는 경우가 생깁니다.

이를 유효성검사라고 한다.

DOM만을 이용해서 style을 바꿀 수 있지만 CSS 클래스를 이용해서 간접적으로 바꾸는 것을 권장한다.. 그 이유는 무엇일까?

  1. 디자인의 디테일한 요소가 자바스크립트 코드에 담기는 것을 방지하기 위해
  2. 따로 클래스를 이용하면, 해당 디자인을 디자이너가 쉽게 바꿀 수 있기 때문에
  3. CSS는 디자인, 자바스크립트는 로직에 집중할 수 있게 하기 위해

이를 한마디로 표현하면 "관심사 분리 때문이다"라고 말할 수 있습니다.

// [유효성 검증 함수]: 영어 또는 숫자만 가능
function onlyNumberAndEnglish(str) {
  return /^[A-Za-z][A-Za-z0-9]*$/.test(str);
}

// [유효성 검증 함수]: 최소 8자 이상하면서, 알파벳과 숫자 및 특수문자(@$!%*#?&) 는 하나 이상 포함
function strongPassword(str) {
  return /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/.test(str);
}

출처
https://ko.javascript.info/searching-elements-dom
https://www.youtube.com/watch?v=zyz1eJJjsNE
코드스테이츠

profile
교사에서 개발자로

0개의 댓글