HTML
페이지에 있는 html, head, body, title, h1, div, span 등을 HTML 언어에서는 요소
라고 부릅니다.
그리고 자바스크립트에서는 이를 문서 객체
라고 부릅니다. 따라서 '문서 객체를 조작한다'는 말은
HTML 요소들을 조작한다 는 의미입니다.
문서 객체를 조합해서 만든 전체적인형태를 문서 객체 모델
이라고 부릅니다. 문서 객체를 조작하는 작업을
하나하나 파고들면 매우 복잡합니다. 요즘은 제이쿼리
와 같은 라이브러리와 리액트
와 같은
프레임워크를 사용하기 때문에 문서 객체 조작이 쉬워졌습니다.
문서 객체를 조작할 때는 DOMContentLoaded
이벤트를 사용합니다. 왜 이코드를 사용해야 하는지
알아봅시다.
코드를 입력할 때DOMContentLoaded
문자열은 오탈자를 입력해도 오류를 발생하지 않습니다.
주의해서 입력해주세요.
document.addEventListener('DOMContentLoaded', () => {
// 문장
})
HTML 페이지는 코드를 위에서 아래로 차례대로 실행합니다.
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<body>
</body>
</html>
따라서 위와 같은 HTML 코드가 있다면 웹 브라우저가 문서의 출력을 빠르게 하기 위해서 일부 먼저
실행하는 부분도 있지만, 기본적으로 다음과 같이 실행한다고 보면 됩니다.
만약 body 태그가 생성되기 이전인 head 태그에서 body 태그에 무언가를 출력하려고 하면
어떻게 될까요? 분명 문제가 발생합니다.
<!DOCTYPE html>
<html>
<head>
<title>DOMContentLoaded</title>
<script>
// HTML 태그를 쉽게 만들 수 있는 콜백 함수를 선언합니다.
const h1 = (text) => `<h1>${text}</h1>`
</script>
<script>
document.body.innerHTML += h1('1번째 script 태그')
</script>
</head>
<body>
<script>
document.body.innerHTML += h1('2번째 script 태그')
</script>
<h1>1번째 h1 태그</h1>
<script>
document.body.innerHTML += h1('3번째 script 태그')
</script>
<h1>2번째 h2 태그</h1>
</body>
</html>
화면에 문자열들이 나타나는데, body 태그가 생성되기 이전에 head 태그 안의 script 태그에서
body 태그를 조작하던 부분 (<h1>
1번째 script 태그</h1>
를 출력하는 부분)은 화면에 출력되지 않습니다.
정리하면 기본적으로 head 태그에 내부에서 script 태그를 배치하면 body 태그에 있는 문서 객체(요소)
에 접근할 수 없습니다.
head 태그 내부의 script 태그에서 body 태그에 있는 문서에 접근하려면 화면에 문서 객체를 모두
읽어들일 때까지 기다려야 합니다.
DOMContentLoaded
이벤트는 웹 브라우저가 문서 객체를 모두 읽고 나서 실행하는 이벤트 입니다.
다음과 같이 코드를 구성하면 DOMContentLoaded
상태가 되었을 때 콜백 함수를 호출합니다.
<script>
// DOMContentLoaded 이벤트를 연결합니다.
document.addEventListener('DOMContentLoaded', () => {
const h1 = (text) => `<h1>${text}</h1>`
document.body.innerHTML += h1('DOMContentLoaded 이벤트 발생')
})
</script>
document.body
코드를 사용하면 문서의 body 요소를 읽어들일 수 있습니다. 이외에도
HTML 문서에 있는 head 요소와 title 요소 등은 다음과 같은 방법으로 읽어들일 수 있습니다.
document.head
document.body
document.title
이는 웹 브라우저의 자바스크립트가 "당연히 있겠지"라고 전제하고 만든 속성입니다. 우리가 head 요소와
body 요소 내부에 만든 다른 요소들은 다음과 같은 별도의 메소도를 사용해서 접근합니다.
document.querySelector(선택자)
document.querySelectorAll(선택자)
선택자 부분에는 CSS 선택자를 입력합니다. CSS 선택자는 매우 다양합니다.
querySelector()
메소드와 querySelectorALl()
메소드를 실펴보겠습니다.
querySelector()
메소드는 요소를 하나만 추출하고, querySelectorAll()
메소드는
문서 객체를 여러 개 추출합니다.
<script>
document.addEventListener('DOMContentLoaded', () => {
// 요소를 읽어들입니다.,
const header = document.querySelector('h1')
// 텍스트와 스타일을 변경합니다.
header.textContent = 'HEADERS'
header.style.color = 'white'
header.style.backgroundColor = 'black'
header.style.padding = '10px'
})
</script>
이어서 querySelctorAll()
메소드 입니다. 일반적으로 forEach()
메소드를 사용해서 반복을 돌립니다.
<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
document.addEventListener('DOMContentLoaded', () => {
// 요소를 읽어들입니다.
const headers = document.querySelectorAll('h1')
// 텍스트와 스타일을 변경합니다.
headers.forEach((header) => {
header.textContent = 'HEADERS'
header.style.color = 'white'
header.style.backgroundColor = 'black'
header.style.padding = '10px'
})
})
</script>
</head>
<body>
<h1></h1>
<h1></h1>
<h1></h1>
<h1></h1>
</body>
</html>
지금까지 살펴본 예제들에서 innerHTML 속성과 textContent 속성을 사용해서 문서 객체 내부의 글자를
조작했습니다.
문서 객체.textContent
: 입력된 문자열을 그대로 넣습니다.
문서 객체.innerHTML
: 입력된 문자열을 HTML 형식으로 넣습니다.
textContent 속성은 입력된 문자열을 그대로 넣어주고,innerHTML 속성은 입력된 문자열을
HTML 형식으로 넣어줍니다.
<script>
document.addEventListener('DOMContentLoaded', () => {
const a = document.querySelector('#a')
const b = document.querySelector('#b')
a.textContent = '<h1>textContent 속성</h1>'
b.innerHTML = '<h1>innerHTML 속성</h1>'
})
</script>
</head>
<body>
<div id="a"></div>
<div id="b"></div>
</body>
문서 객체의 속성을 조작할 때는 다음과 같은 메소드를 사용합니다.
문서 객체.setAttribute(속성 이름, 값)
: 특정 속성에 값을 지정합니다.
문서 객체.getAttribute(속성 이름)
: 특성 속성을 추출합니다.
문서 객체의 스타일을 조작할 때는 style 속성을 사용합니다. style 속성은 객체이며, 내부에는 속성으로 CSS를 사용해서 지정할 수 있는 스타일들이 있습니다.
css 속성 이름 | 자바스크립트 style 속성 이름 |
---|---|
background-color | backgroundColor |
text-align | textAlign |
font-size | fontSize |
25개의 div태그를 조작해서 검은색에서 흰색으로 변화하는 그레이디언트를 만드는 코드
<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
document.addEventListener('DOMContentLoaded', () => {
const divs = document.querySelectorAll('body > div')
divs.forEach((div, index) => {
console.log(div, index)
const val = index * 10
div.style.height = `10px`
div.style.backgroundColor = `rgba(${val}, ${val}, ${val})`
})
})
</script>
</head>
<body>
<!-- div 태그 25개 -->
<div></div><div></div><div></div><div></div><div></div>
<div></div><div></div><div></div><div></div><div></div>
<div></div><div></div><div></div><div></div><div></div>
<div></div><div></div><div></div><div></div><div></div>
<div></div><div></div><div></div><div></div><div></div>
<div></div><div></div><div></div><div></div><div></div>
</body>
</html>
문서 객체를 생성하고 싶을 때에는 document.createElement()
메소드를 사용합니다.
document.createElement(문서 객체 이름)
문서를 어떤 문서 아래에 추가 할지를 지정해줘야 합니다. 이러한 그림을 프로그래밍에서는 트리 라고 부릅니다. 어떤 문서 객체가 있을 때 위에 있는 것을 부모 라고 부르고, 아래에 있는 것을 자식이라고 부릅니다.
문서 객체에는 appendChild()
메소드가 있으며, 이를 활용하면 어던 부모 객체에 자식 객체를
추가할 수 있습니다. 문서 객체 트리 구조를 만드는 방법은 다음과 같습니다.
부모 객체.appendChild(자식 객체)
<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
document.addEventListener('DOMContentLoaded', () => {
// 문서 객체 생성하기
const header = document.createElement('h1')
// 생성한 태그 조작하기
header.textContent = '문서 객체 동적으로 생성하기'
header.setAttribute('data-custom', '사용자 정의 속성')
header.style.color = 'white'
header.style.backgroundColor = 'black'
// h1 태그를 body 태그 아래에 추가하기
document.body.appendChild(header)
})
</script>
</head>
<body>
</body>
</html>
appendChild()
메소드는 문서 객체를 이동할 때도 사용할 수 있습니다. 문서 객체의 부모는 언제나 하나여야 합니다. 따라서 문서 객체를 다른 문서 객체에 추가하면 문서 객체가 이동합니다.
<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
document.addEventListener('DOMContentLoaded', () => {
// 문서 객체 읽어들이고 생성하기
const divA = document.querySelector('#first')
const divB = document.querySelector('#second')
const h1 = document.createElement('h1')
h1.textContent = '이동하는 h1 태그'
// 서로 번갈아가면서 실행하는 함수를 구현합니다.
const toFirst = () => {
divA.appendChild(h1) // h1을 divaA에 추가합니다.
setTimeout(toSecond, 1000) // 1초 뒤에 tosecond 함수를 실행합니다
}
const toSecond = () => {
divB.appendChild(h1) // h1을 divB에 추가합니다.
setTimeout(toFirst, 10000) // 10초 뒤에 toFirst 함수를 실행합니다.
}
toFirst()
})
</script>
</head>
<body>
<div id="first">
<h1>첫 번째 div 태그 내부</h1>
</div>
<hr>
<div id="second">
<h1>두 번째 div 태그 내부</h1>
</div>
</body>
</html>
문서 객체를 제거할 때는 removeChild()
메소드를 사용합니다.
부모 객체.removeChild(자식 객체)
appendChild() 메소드 등으로 부모 객체와 이미 연결이 완료된 문서 객체의 경우 parentNode속성으로
부모 객채에 접글한 수 있으므로,일반적으로 어떤 문서 객체를 제거할 때는 다음과 같은 형태의 코드를
사용합니다.
문서 객체.parentNode.removeChild(문서 객체)
<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
const h1 = document.querySelector('h1')
h1.parantNode.removeChild(h1)
// document.body.removeChild(h1)
}, 3000)
})
</script>
</head>
<body>
<hr>
<h1>제거 대상 문서 객체</h1>
<hr>
</body>
</html>
모든 문서 객체는 생성되거나 클릭되거나 마우스를 위에 올리거나 할때 이벤트
라는 것이 발생합니다.
그리고 이 이벤트가 발생할 때 실행할 함수는 addEventListener()
메소드를 사용합니다.
문서 객체.addEventListener(이벤트 이름,콜백 함수)
이벤트가 발생할때 실행할 함수를 이벤트 리너스
또는 이벤트 핸들러
라고 부릅니다.
<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
document.addEventListener('DOMContentLoaded', () => {
let counter = 0
const h1 = document.querySelector('h1')
h1.addEventListener('click', (event) => {
counter++
h1.textContent = `클릭 횟수:${counter}`
})
})
</script>
<style>
h1{
/* 클릭을 여러 번 했을 때
글자가 선택되는 것을 막기 위한 스타일 */
user-select: none;
}
</style>
</head>
<body>
<h1>클릭 횟수: 0</h1>
</body>
</html>
이벤트를 제거할 때는 다음과 같은 형태로 removeEventListener()
메소드를 사용합니다
문서 객체.removeEventListener(이벤트 이름, 이벤트 리너스)
이벤트 리스너가 여러 번 연결되지 않게 isConnect
라는 변수를 활용했습니다.
<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
document.addEventListener('DOMContentLoaded', () => {
let counter = 0
let isConnect = false
const h1 = document.querySelector('h1')
const p = document.querySelector('p')
const connectButton = document.querySelector('#connect')
const disconnectButton = document.querySelector('#disconnect')
const listener = (event) => {
h1.textContent = `클릭 횟수: ${counter++}`
}
connectButton.addEventListener('click', () => {
if (isConnect === false) {
h1.addEventListener('click', listener)
p.textContent = '이벤트 연결 상태: 연결'
isConnect = true
}
})
disconnectButton.addEventListener('click', () => {
if (isConnect === true) {
h1.removeEventListener('click', listener)
p.textContent = '이벤트 연결 상태: 해제'
}
})
})
</script>
<style>
h1{
/* 클릭을 여러 번 했을 때
글자가 선택되는 것을 막기 위한 스타일 */
user-select: none;
}
</style>
</head>
<body>
<h1>클릭 횟수: 0</h1>
<button id="connect">이벤트 연결</button>
<button id="disconnect">이벤트 제거</button>
<p>이벤트 연결 상태: 해제</p>
</body>
</html>
이벤트를 연결하는 방법을 이벤트 모델
이라고 부릅니다.
07-1 에서 이벤트를 연결할 때 addEventListener()
메소드를 사용했습니다.
현재 이 방법이 현재 표준으로 사용하고 있는 방법이므로 표준 이벤트 모델
이라고 부릅니다.
document.body.addEventListener('keyup', () => { })
과거에는 다음과 같이 문서 객체가 갖고 있는 on떙땡 으로 시작하는 속성에 함수를 할당해서 이벤트를
연결했습니다.이와 같은 이벤트 연결 방법을 고전 이벤트 모델
이라고 부릅니다.
document.body.onkeyup = (event) +> { }
on땡땡으로 시작하는 속성을 HTML 요소에 직접 넣어서 이벤트를 연결하는 것을 인라인 이벤트 모델
이라고 부릅니다.
<script>
const listener = (event) => {
}
</script>
<body onkeyup = "listener(event)">
</body>
keydown
: 키가 눌릴 때 실행됩니다. 키보드를 꾹 누르고 있을 때도, 입력될 때도 실행됩니다.
keypress
: 키가 입력되었을 때 실행됩니다.
keyup
: 키보드에서 키가 떨어질 때 실행됩니다.
<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
document.addEventListener('DOMContentLoaded', () => {
const textarea = document.querySelector('textarea')
const h1 = document.querySelector('h1')
textarea.addEventListener('keyup', (event) => {
const length = textarea.value.length
h1.textContent = `글자 수: ${length}`
})
})
</script>
</head>
<body>
<h1></h1>
<textarea></textarea>
</body>
</html>