“HTML 요소를 Object(JavaScript)처럼 조작(Manipulation)할 수 있는 Model”
"Document Object Model"의 약자
DOM으로 HTML을 조작할 수 있음
즉, HTML로 구성된 웹페이지를 동적으로 움직이게 만들 수 있음
HMTL에 JavaScript를 적용하기 위해서 <script>
태그를 이용함
<script src="myScriptFile.js"></script>
파싱은 브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것을 의미함
웹브라우저가 작성된 코드를 해석하는 과정에서
<script>
요소를 만나면 웹 브라우저는 HTML 해석을 잠시 멈춤
HTML 해석을 잠시 멈춘 웹 브라우저는 <script>
요소를 먼저 실행함
<script>
요소는 등장과 함께 실행된다는 사실을 꼭 기억!!
script
요소를 추가하는 대표적인 사례 2가지// myScriptFile.js 파일 내용
console.log("welcome JavaScript");
let msgElement = document.querySelector("#msg");
console.log(msgElement);
1️⃣ <head>
안쪽에 삽입하는 경우
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
<!-- script 요소 삽입 위치 -->
<script src="myScriptFile.js"></script>
</head>
<body>
<div id="msg">Hello JavaScript!</div>
</body>
</html>
화면 | 콘솔 |
---|---|
Hello JavaScript! | welcome JavaScript null |
2️⃣ </body>
요소가 끝나기 전에 삽입하는 경우
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div id="msg">Hello JavaScript!</div>
<!-- script 요소 삽입 위치 -->
<script src="myScriptFile.js"></script>
</body>
</html>
화면 | 콘솔 |
---|---|
Hello JavaScript! | welcome JavaScript <div id="msg">Hello JavaScript!</div> |
두 방식 모두 myScriptFile.js 내의 첫번째 console.log를 성공적으로 출력하지만
두 번째 console.log가 제대로 출력되지 않음
Q. 각 방법은 콘솔 출력이 어떻게 다른가요?
<head>
안쪽에 삽입하는 경우 : null
</body>
요소 전에 삽입하는 경우 : <div id="msg">Hello JavaScript!</div>
Q. 1번 방법에서는 null 이 콘솔에 출력되는 이유가 무엇일까요?
HTML 파싱 과정에서 <script>
를 만나면 <script>
가 먼저 실행되는데
이때, 자바스크립트에서 아직 읽히지 않은 html 요소에
접근하는 코드가 작성되어 있기 때문에 null
이 출력됨
Q. HTML 요소를 이용하려면 1번과 2번 중 어떤 방법을 사용해야 할까요?
</body>
요소가 끝나기 전에 삽입해야
HTML 코드를 전부 읽은 후 <script>
가 실행됨
HTML은 단순히 규칙에 따라 정해진 태그, 속성값으로 이루어진 언어이며,
DOM은 브라우저가 HTML 파싱한 후 생성되는 객체 모델로, document에 접근가능한 API
API
: application programming interface
운영체제와 응용프로그램 사이의 통신에 사용되는 언어나 메시지 형식
HTML 문서를 파싱하는 과정에서 단순 텍스트로 구성된 HTML문서의 내용과 구조가 객체 모델(DOM)으로 변환되어 다양한 프로그램에서 사용될 수 있음
DOM 구조는 회사의 조직도와 유사
<body>
가 가장 상위에 있고 아래에 여러 구성요소가 부모-자식 관계를 가지고 있음
이런 자료 구조를 컴퓨터 공학에서는 트리 구조라고 함.
트리 구조는 부모가 자식을 여러 개 가지고, 각각의 자식이 다시 자식을 갖는 구조가 반복되는 것이 특징
만약, 부모가 가진 하나 또는 여러 개의 자식 엘리먼트를 조회하는 코드를 작성한다면
여러 번 반복해서 실행하는 코드가 필요
예시
다음 HTML 문서에서 id의 이름이nav
인<div>
요소를 포함해서, 모든 자식 요소의class
이름을console.log
를 사용하여 확인하는 방법은 무엇인지 의사코드로 작성해보자<html> <body> <div id="nav"> <div class="logo"></div> <div class="menu-wrapper"> <div class="menu"></div> <div class="menu"></div> <div class="menu"></div> <div class="profile-photo"></div> </div> </div> <div id="news-contents"> <div class="news-content-wrapper"> <div class="news-picture"></div> <div class="news-title"></div> <div class="news-description"></div> </div> </div> <div id="footer"></div> </body> </html>
⬇️ 엘리먼트를 조회하는 코드를 작성하기 위한 수도코드
function consoleLogAllElement(element){ // nav의 class 이름을 console.log 합니다. // nav의 자식 엘리먼트가 있는지 검색합니다. (logo, menu-wrapper) //logo의 class 이름을 console.log 합니다. //logo의 자식 엘리먼트가 있는지 검색합니다. (없음) //menu-wrapper의 class 이름을 console.log 합니다. //menu-wrapper의 자식 엘리먼트가 있는지 검색합니다. (menu, menu, menu, profile-photo) //첫 번째 menu의 class 이름을 console.log 합니다. //첫 번째 menu의 자식 엘리먼트가 있는지 검색합니다. (없음) //두 번째 menu의 class 이름을 console.log 합니다. //두 번째 menu의 자식 엘리먼트가 있는지 검색합니다. (없음) //세 번째 menu의 class 이름을 console.log 합니다. //세 번째 menu의 자식 엘리먼트가 있는지 검색합니다. (없음) //profile-photo의 class 이름을 console.log 합니다. //profile-photo의 자식 엘리먼트가 있는지 검색합니다 (없음) //자식 엘리먼트를 모두 탐색했음으로, 함수 실행이 종료됩니다. //자식 엘리먼트를 모두 탐색했음으로, 함수 실행이 종료됩니다. }
자바스크립트에서 DOM은 document
객체에 구현되어 있음
브라우저에서 작동하는 모든 자바스크립트 코드에서는 document
객체를 조회할 수 있음
Document
: 브라우저가 불러온 웹 페이지를 나타내며, DOM 트리의 진입점 역할
DOM 구조를 조회할 때는 console.log
보다 console.dir
이 유용함
console.dir
은 console.log
와 달리 DOM을 객체의 모습으로 출력함
console.log
와 console.dir
의 차이body
를 조회했을 때 나오는 수많은 속성들은
HTML 요소에 지정할 수 있었던 다양한 속성이 이미 객체 내에 존재한다고 보면 됨
<body>
요소의 자식 요소 개수 알아보기document.body
객체의 키 중에서 children
을 찾으면 됨
children
속성의 자식 요소까지 확인 가능console.dir(document.body)
를 통해 출력된 객체에서 children
속성을 찾을 수 있음
children
속성에 nav
, news-contents
, footer
가 자식으로 있는 것도 확인 가능
물론 document.body.children
으로 바로 조회할 수도 있음!
id
가 news-contents
인 <div>
요소의 부모 요소를 찾기.parentElement
속성을 이용!
"id가 news-contents
인 <div>
요소"는 <body>
요소의 자식 요소
반대로 <body>
요소는 "id가 news-contents
인 div
요소"의 부모 요소
id가 news-contents
인 엘리먼트를 조회하려면,
document.body.children
의 첫번째 요소를 조회해야 함
document.body.children
의 요소를 확인하고, id가 news-contents
인 엘리먼트를 조회이때, document.body
의 children
을 조회할 때마다,
매번 document.body
로부터 찾아가는 일은 매우 번거로운 일
하나의 변수를 선언해서 이 정보를 저장해두면 주소를 참조하기 때문에 언제든지 접근할 수 있음!
변수
newsContents
를 선언하여id
가news-contents
인 요소를 할당한다let newsContents = document.body.children[1]
document.body.children[1]
를 변수 newsContents
에 할당해주면 간단하게 접근 가능newsContents의 부모 요소를 찾아보자!
document.body.children[1].parentElement
// or
newsContents.parentElement
//document.body.children[1]를 변수 newsContents에 할당해줬다면
앞서 확인할 수 있듯이, document
객체에는 많은 속성과 메서드가 존재함
그러나 모든 속성과 메서드를 외워야 할 필요는 없으며
지금 가장 집중해야할 부분은 CRUD (Create, Read, Update and Delete)
CRUD를 먼저 이해하는 것이 새로운 언어를 가장 빠르게 학습하는 방법!
document
객체를 통해서 HTML 엘리먼트를 만들고(Create), 조회하고(Read), 갱신하고(Update), 삭체하는(Delete) 방법을 알아보자
* DOM에는 HTML에 적용하는(Append) 메서드가 따로 있으니 주의!
새로운 <div>
요소 만들기
document.createElement('div')
<div>
를 만들어 줌새롭게 생성한 div
element를 활용하기 위해서는?
자바스크립트에서 어떤 작업의 결과를 담으려면 변수를 선언하고 작업의 결과를 변수에 할당해야함
변수 tweetDiv에 새롭게 생성한
<div>
element를 할당한다const tweetDiv = document.createElement('div')
아직은 화면에 아무런 변화가 없음
tweetDiv 라는 요소는 아무것도 연결되지 않은 하나의 노드인 상태
공중에 떠있는 엘리먼트를 웹페이지에서 확인하기 위해서는 APPEND 해야함
APPEND를 이용해 실제 웹페이지 상에도 보이는 것을 확인할 수 있음
append
메서드를 사용하여 포함할 수 있음
변수 tweetDiv에 담긴 새로운
<div>
요소를<body>
요소에 append한다document.body.append(tweetDiv)
그러나 아직 <div>
요소에 내용을 입력하지 않아서 보이는 내용이 없는 상태
크롬의 개발자 도구 Elements 탭에서 변경사항을 확인할 수 있음
<div>
요소가 <body>
요소에 포함된 걸 확인할 수 있음다른 트윗처럼 이쁘게 container 안에 넣기 위해서는?
생성된 tweetDiv
를 container
에 넣기 위해서는, container
를 먼저 찾아야 함
자바스크립트에서 원시 자료형인 변수의 값을 조회할 땐, 변수의 이름으로 직접 조회할 수 있으며
참조 자료형인 배열은 index를, 객체는 key를 이용해 값을 조회할 수 있음
DOM으로 HTML 엘리먼트의 정보를 조회하기 위해서는
querySelector
의 첫번째 인자로 셀렉터(selector)를 전달하여 확인할 수 있음
셀렉터로는 HTML 요소(“div”), id(“#tweetList”), class(.tweet) 세 가지가 가장 많이 사용 됨
querySelector
: 셀렉터를 조회한다는 의미. 'query'의 의미가 ‘질문하다’라는 것을 알고 있다면 역할을 쉽게 유추 가능!
이 'query' 라는 단어는 개발자 간에 “ㅇㅇㅇ를 조회한다” 라는 의미가
“쿼리를 날리다” 라는 jargon(특정 영역에서만 사용되는 단어)으로 굳어졌기 때문에 알고 있어야 함
querySelector
에 ‘.tweet’
을 첫 번째 인자로 넣으면,
class 이름이 tweet
인 HTML 엘리먼트 중 첫번째 엘리먼트를 조회할 수 있음
querySelector
로 class 이름이tweet
인 HTML요소를 조회한다const oneTweet = document.querySelector('.tweet')
HTML 문서에는 class 이름이 tweet
인 요소가 여러 개 있는데,
변수 oneTweet
에 할당된 요소는 단 하나임
(중복인 경우 querySelector
는 최상단 엘리먼트만을 조회함)
여러 개의 요소를 한 번에 가져오기 위해서는 querySelectorAll
을 사용함
이렇게 조회한 HTML 요소들은 배열처럼 for문을 사용할 수 있음
주의! 앞서 조회한 HTML요소들은 배열이 아님
이런 ‘배열 아닌 배열’을 유사 배열, 배열형 객체 등 다양한 이름으로 부름
정식 명칭은 Array-like Object
querySelectorAll
로 class 이름이tweet
인 모든 HTML 요소를 유사 배열로 받아온다const tweets = document.querySelectorAll('.tweet')
querySelector
와 querySelectorAll
만 알아도 대부분의 요소를 조회할 수 있음
그러나 후에 개발자로 입사했을 때 get으로 시작하는 DOM 조회 메서드를 볼 수도 있음
get~ 메서드는 querySelector
와 비슷한 역할을 하는 오래된 방식임
(인터넷 익스플로러 호환성을 고려했을 때 이런 방식을 사용)
실제 동작은 동일하니 이런 메서드가 있다는 것은 알아두기 !
getElementById
와querySelector
로 각각 받아 온container
요소는 하나의 요소이다const getOneTweet = document.getElementById('container') const queryOneTweet = document.querySelector('#container') console.log(getOneTweet === queryOneTweet) // true
getElementById
와 querySelector
로 받아 온 container
요소가 일치하는 모습이로써 생성할 요소를 container
에 넣을 준비를 마치게 됨
아래의 코드를 입력하면 container
의 맨 마지막 자식 요소로 tweetDiv
를 추가함
tweetDiv
를container
의 마지막 자식 요소로 추가한다const container = document.querySelector('#container') const tweetDiv = document.createElement('div') container.append(tweetDiv)
tweetDiv
를 container
의 마지막 자식 요소로 추가한 모습현재 새롭게 추가된 tweetDiv
는 별도의 class가 지정되어 있지 않아,
CSS를 이용한 스타일링이 적용되지 않음
기존에 생성한 빈 div
태그를 업데이트하여, 보다 다양한 작업을 할 수 있음
oneDiv
라는 이름의<div>
요소를 만든다const oneDiv = document.createElement('div'); console.log(oneDiv) // <div></div>
textContent
를 이용해 비어있는<div>
엘리먼트에 문자열을 입력한다oneDiv.textContent = 'dev'; console.log(oneDiv) // <div>dev</div>
앞서 생성한 <div>
엘리먼트를 container
에 append
했을때
CSS 스타일링이 적용되지 않았음
CSS 스타일링이 적용될 수 있도록 <div>
엘리먼트에 class
를 추가
classList.add
를 이용해tweet
클래스를 추가한다oneDiv.classList.add('tweet') console.log(oneDiv) // <div class="tweet">dev</div>
현재 생성한 엘리먼트에 텍스트를 채우고, 클래스를 추가하여 스타일링을 적용한 상태
이번에는 append
를 이용해 container
의 자식 요소로 추가
append를 이용해 container의 자식 요소에 oneDiv를 추가한다
const container = document.querySelector('#container') container.append(oneDiv)
oneDiv
에 tweet
클래스를 추가하여 스타일링한 후 container
의 자식 요소로 추가한 모습Q. class와 id말고 다른 attribute를 추가할 수는 없나요?
element.setAttribute(name, value)
를 사용하여 가능
지정된 요소의 속성 값을 설정
속성이 이미 있는 경우 값이 업데이트됨
// 예시
element.setAttribute('title', 'newTitle')`
: title 속성의 값을 newTitle로 설정
element.removeAttribute()
: 요소의 속성을 제거하는 메서드
1️⃣ 삭제하려는 요소의 위치를 알고 있는 경우
remove
메서드 사용
id가
container
인 요소 아래에tweetDiv
를 추가하고,remove
로 삭제한다const container = document.querySelector('#container') const tweetDiv = document.createElement('div') container.append(tweetDiv) tweetDiv.remove() // 이렇게 append 했던 요소를 삭제할 수 있다.
2️⃣ 여러 개의 자식 요소를 삭제하려는 경우
innerHMTL
을 이용
id
가container
인 요소 아래의 모든 요소를 지운다document.querySelector('#container').innerHTML = '';
그러나 innerHTML
은 몇 가지 보안 문제를 가지고 있음
removeChild()
을 이용removeChild
는 자식 요소를 지정해서 삭제하는 메서드
container
의 첫 번째 자식 요소가 존재하면, 첫번째 자식 요소를 제거한다const container = document.querySelector('#container'); while (container.firstChild) { container.removeChild(container.firstChild); }
removeChild
메서드와 while
문을 활용하여 자식 요소를 삭제한 모습removeChild와 while을 이용해 자식 요소를 삭제하면,
제목에 해당하는 H2
"Tweet List"까지 삭제됨
이를 방지하기 위한 방법은 다양함
1️⃣ 자식 요소가 담고 있는 문자열을 비교해 "Tweet List"만 남긴다
2️⃣ 새로운 변수에 "Tweet List"를 할당해뒀다가 반복문이 끝난 뒤에 새롭게 추가한다
3️⃣ 자식 요소가 1개만 남을 때까지 마지막 자식 요소를 제거한다
4️⃣ 직접 클래스 이름이 tweet
인 요소만 찾아서 지운다
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() }
새로운 요소 만들기
createElement(만들 요소)
document.createElement(‘div’)
요소를 DOM으로 조작하기 위해 변수에 할당
const tweetDiv = document.createElement('div')
요소 넣기
.append(넣을 요소)
document.body.append(tweetDiv)
요소 조회하기
.querySelector(요소/class/id)
const oneTweet = document.querySelector('.tweet')
// 클래스 tweet을 조회하여 변수에 할당
여러개의 요소를 한번에 조회하여 유사 배열로 받아오기
.querySelectorAll(요소/class/id)
const tweets = document.querySelectorAll('.tweet')
엘리먼트에 문자열을 입력
element.textContent = ‘문자열’
oneDiv.textContent = 'dev';
클래스 추가하기
추가받는el.classList.add(추가하려는 class)
oneDiv.classList.add('tweet')
요소의 속성 값 설정
element.setAttribute(name, value)
element.setAttribute('title', 'newTitle')
// title 속성의 값을 newTitle로 설정
위치를 알고 있는 요소 삭제하기
경로.remove()
const container = document.querySelector('#container')
const tweetDiv = document.createElement('div')
container.append(tweetDiv)
tweetDiv.remove() // 이렇게 append 했던 요소를 삭제할 수 있다.
모든 자식 요소 삭제
element.innerHTML = ''
활용
document.querySelector('#container').innerHTML = '';
// 클래스 container의 HTML태그를 ''로 만듦 = 지움
모든 자식 요소 삭제
parent.removeChild(child)
활용
const container = document.querySelector('#container');
while (container.firstChild) {
container.removeChild(container.firstChild);
}
// 첫번째 자식 요소가 존재하면, 첫번째 자식 요소 제거
여러 개의 자식 요소 삭제
자식 요소가 1개 남을 때까지 마지막 자식 요소 제거
const container = document.querySelector('#container');
while (container.children.length > 1) {
container.removeChild(container.lastChild);
}
여러 개의 자식 요소 삭제
클래스 이름에 해당하는 요소만 제거
const tweets = document.querySelectorAll('.tweet')
tweets.forEach(function(tweet){
tweet.remove();
})
// or
for (let tweet of tweets){
tweet.remove()
}
갑자기 엄청난 양의 낯선 개념들과... 낯선 메서드... 낯선 속성...
낯선 것들이 쏟아져서 허겁지겁 공부한 하루였다
DOM에 대한 개념이 이해하기 어려워서 구글링해서 나온 사이트 10개 정도 정독하고나니까 얼추 개념이 잡히는 것 같다. 해외 문서들을 번역하고, 정리해주신 수많은 블로거님들 정말 감사합니다..
그러나 이렇게 했는 데도 공부해야 할 개념들이 산더미다...
주말에 난리났다... 진자로
부트캠프 수강한 뒤로 주말이 주말이 아니다...😂
아무튼 어렵긴 했지만, 변화가 즉각적으로 눈에 보이는 작업을 하니 재밌었다!
오늘 DOM에 대해 학습하고 유효성 검사까지 실습했는데, 유효성 검사 내용까지 담기에는 페이지가 너무 길어질 것 같아 내일 내용에 한번에 적으려 한다
내일은 오늘 제작한 회원가입 페이지를 바탕으로 CSS 바꾼다
벌써 기대된다 내일도 화이팅 ~~
더 공부하기!
심화 학습 목표 보기
forEach()이거 뭔지
참고하면 좋은 사이트들 !!
브라우저는 어떻게 동작하는가?
https://d2.naver.com/helloworld/59361
DOM 쉽게 이해하기
https://kingofbackend.tistory.com/25