혼자공부하는 자바스크립트 유투브 동영상 강의와 책을 보며 정리합니다.
#혼공JS #혼공단 #혼공챌린지
Chapter 07 문서 객체 모델
: DOMContentLoaded 이벤트를 사용하여 문서 객체를 조작해보고 다양한 이벤트의 사용 방법을 이해합니다.
문서 객체 모델 (Document Object Model -> DOM)
문서 객체 : HTML 요소
문서 객체 모델 : 그걸 조작하는 객체들의 집합
결과
A는 출력되지 않고 오류가 뜬다. 그 이유는 저 시점에서는 아직 body 태그가 아직 안만들어 졌기 때문에 body를 조작할 수 없기 때문
DOMContentLoaded
를 왜 쓰냐?
document.addEventListener('DOMContentLoaded', () => {})
이다
문서 객체를 조작할 수 있는 위치:
1.head 테그 속 script 태그 속 DOMContentLoaded
내부 속
2.body 태그 내부의 최하단 script 태그 속
태그를 읽어들이는 메서드. 쿼리셀렉터는 해당하는것 하나만 선택한다. () 안의 해당하는 것들이 여러개일 경우 최상위에 있는 것 하나만 선택됨
클래스를 두개 이상 사용할 때 .으로 연결해주고 사이에 띄워쓰기를 넣으면 안된다!
태그 안의 태그 를 선택할 때
'태그'
'#아이디'
'.클래스'
'[타입=텍스트]'
결과
querySelector 는 조건에 맞는 것들 중 가장 처음것 하나만 선택한다
여러개의 태그를 잡고싶은 경우에 사용.
보통 textContent
를 많이 사용하고, innerHTML
은 보안상의 문제로 많이 사용하지 않는다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
const header1 = document.querySelector('#textContent');
const header2 = document.querySelector('#innerHTML');
// 조작!
// 값을 추출한다
console.log(header1.textContent);
console.log(header2.innerHTML);
// 값을 넣는다
header1.textContent = '넣고싶은<br>문자열';
header2.innerHTML = '넣고싶은<br>문자열2';
})
</script>
</head>
<body>
<h1 id = "textContent">textContent 속성 기존 문자열</h1>
<h1 id = "innerHTML"> innerHTML 속성 기존 문자열</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
// 조작!
const img = document.querySelector('img');
// 값을 넣는다
img.setAttribute('src','')
// 값을 추출한다
})
</script>
</head>
<body>
<img src="" alt="">
</body>
</html>
-
을 쓸 수 없으므로 - 대신 중간에 대문자를 활용해서 작성한다.
-을 쓰기 원한다면 아래처럼 []을 사용할 수도 있으나 저렇게 -를 사용하면 자동완성이 안되기 때문에 잘 쓰지는 않는다!
div.style.height = 10
이렇게 단위를 빼주면 정상적으로 동작하지 않는다!
<script>
document.addEventListener('DOMContentLoaded', () => {
//요소를 만든다
const header = document.createElement('hi')
header.textContent = 'createElement로 만든 태그'
header.style.color = 'red'
const body = document.querySelector('body')
//요소를 출력 해준다
body.appendChild(header)
})
</script>
요소 제거
setTimeout(()=> {
// body.removeChild(header)
header.parentNode.removeChild(header)
}, 2000)
2초 뒤에 제거가 된다.
두가지 방법이 있지만 parentNode 의 방법을 더 많이 쓴다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
const header = document.createElement('h2')
header.textContent = '안녕하세요'
const first = document.querySelector('.first')
const second = document.querySelector('.second')
first.appendChild(header)
setTimeout(() => {
second.appendChild(header)
}, 2000)
})
</script>
</head>
<body>
<div class= "first">
<h1>첫 번째 div 태그 내부</h1>
</div>
<hr>
<div class= "second">
<h1>두 번째 div 태그 내부</h1>
</div>
</body>
</html>
이동도 별 다를거 없이 append 를 쓰면 된다
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
const header = document.createElement('h2')
header.textContent = '안녕하세요'
const first = document.querySelector('.first')
const second = document.querySelector('.second')
first.appendChild(header)
const toFirst = () => {
first.appendChild(header)
setTimeout(toSecond, 1000)
}
const toSecond = () => {
second.appendChild(header)
setTimeout(toFirst, 1000)
}
toFirst()
})
</script>
</head>
<body>
<div class= "first">
<h1>첫 번째 div 태그 내부</h1>
</div>
<hr>
<div class= "second">
<h1>두 번째 div 태그 내부</h1>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
const header = document.createElement('h2')
header.textContent = '안녕하세요'
const arr = [
document.querySelector('.first'),
document.querySelector('.second'),
]
let counter = 0
console.log(arr[counter % 2]);
const fun = () => {
arr[counter % 2].appendChild(header)
counter ++
}
setInterval(fun, 1000)
fun()
})
</script>
</head>
<body>
<div class= "first">
<h1>첫 번째 div 태그 내부</h1>
</div>
<hr>
<div class= "second">
<h1>두 번째 div 태그 내부</h1>
</div>
</body>
</html>
문서객체를 만들때는 document.createElement
를 활용한다
문서객체를 붙이거나 이동할 때는 appendChild
제거할 때는 header.parentNode.removeChild
사용
<script>
document.addEventListener('DOMContentLoaded', () => {
const header = document.createElement('h1')
document.body.appendChild(header)
let count = 0
header.style.userSelect = 'none'
header.innerText = ` 클릭횟수 : ${count}`
header.addEventListener('click', () =>{
count++
header.innerText = ` 클릭횟수 : ${count}`
})
})
</script>
책과 같이 body 안에 h1 태그를 이미 넣어줘도되고, 저렇게 createElement
를 사용해 추가해줘도 된다.
책과 같이 CSs를 직접 수정해도 되고 , header.style.userSelect = 'none'
를 사용해도 된다.
결과
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const header = document.createElement('h1')
header.innerText = `클릭 횟수 : 0`
const p = document.createElement('p')
p.innerText = `이벤트 연결 상태: 해제`
const connectBtn = document.createElement('button')
connectBtn.innerText = `이벤트 연결버튼`
const disconnectBtn = document.createElement('button')
disconnectBtn.innerText = `이벤트 해제버튼`
document.body.appendChild(header)
document.body.appendChild(connectBtn)
document.body.appendChild(disconnectBtn)
document.body.appendChild(p)
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let counter = 0
const listener = () => {
// counter++
header.textContent = `클릭 횟수 : ${++counter}`
}
// console.log(counter);
const header = document.createElement('h1')
header.textContent = `클릭 횟수 : 0`
const p = document.createElement('p')
p.textContent = `이벤트 연결 상태: 해제`
const connectBtn = document.createElement('button')
connectBtn.textContent = '이벤트 연결버튼'
connectBtn.addEventListener('click', () => {
header.addEventListener('click', listener)
p.textContent = '이벤트 연결 상태: 연결'
})
const disconnectBtn = document.createElement('button')
disconnectBtn.textContent = '이벤트 해제버튼'
disconnectBtn.addEventListener('click', () => {
header.removeEventListener('click', listener)
p.textContent = '이벤트 연결 상태: 해제'
})
document.body.appendChild(header)
document.body.appendChild(connectBtn)
document.body.appendChild(disconnectBtn)
document.body.appendChild(p)
</script>
</body>
</html>
확인문제 2. 요소들을 querySelector() 메소드로 선택 시 사용할 수 있는 선택자들
keydown -> keypress -> 입력양식에 값이 들어감 -> keyup
<script>
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('keydown', () =>{
document.body.innerHTML = '<h1>keydown</h1>'
})
document.addEventListener('keyup', () =>{
document.body.innerHTML = '<h1>keyup</h1>'
})
})
</script>
*** textContent 를 사용하면 동작하지 않는다..
<script>
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('keydown', (event) =>{
document.body.innerHTML = `<h1>keydown ${event.code}</h1>`
})
document.addEventListener('keyup', () =>{
document.body.innerHTML = '<h1>keyup</h1>'
})
})
</script>
누른 키를 보여줌.
어떤 이벤트가 발생 했을 시 그 이벤트와 관련된 변수는 콜백함수의 첫번째 매개변수로 가지고 온다. e 로 짓거나 event 로 해주는 편.
<script>
document.addEventListener('DOMContentLoaded', () => {
const h1 = document.querySelector('h1')
const textarea = document.querySelector('textarea')
textarea.addEventListener('keyup', () => {
h1.textContent = `글자 수 : ${textarea.value.length}`
})
})
</script>
keyup을 쓰기때문에 키를 뗀 후에 글자수가 세어진다.
keypreess를 쓰면 한국어는 카운팅이 안된다.. - 해결법은 나중에 다룬다
아래 코드에서 textarea가 이벤트 발생 객체
textarea.addEventListener('keyup', () => {
h1.textContent = `글자 수 : ${textarea.value.length}`
})
textarea.addEventListener('keyup', (event) => {
h1.textContent = `글자 수 : ${event.currentTarget.value.length}`
})
이렇게 쓰는 것과 같다.
textarea.addEventListener('keyup', function () {
h1.textContent = `글자 수 : ${this.value.length}`
})
그냥 익명함수 + this 키워드를 사용하는 방법도 있다.
위의 3가지 코드 모두 활용이 많이 된다. event.currentTarget
가 그중에서도 가장 중요!
<script>
document.addEventListener('DOMContentLoaded', () => {
const a = document.querySelector('a')
//클릭시 링크 이동 막기
a.addEventListener ('click',(event) => {
event.preventDefault()
})
//우클릭막기
a.addEventListener('contextmenu', (event) => {
event.preventDefault()
})
})
</script>
</head>
<body>
<a href="http://google.com">링크</a>
</body>
<script>
document.addEventListener('DOMContentLoaded', () => {
const buttonA = document.querySelector('button')
const buttonB = document.querySelector('input[type=button]')
buttonA.addEventListener('click', (event) => {
event.currentTarget.textContent += '글자'
})
buttonB.addEventListener('click', (event) => {
event.currentTarget.value += '글자2'
})
})
</script>
</head>
<body>
<!-- click 이벤트 -->
<button>글자</button>
<input type="button" value="글자">
</body>
submit은 form 태그 내부에 위치하여야 한다
button 태그도 input[type=submit]
처럼 동작을 한다
form 태그 내에 button 태그를 넣게 되면 클릭시 submit이 되게 된다
그래서 submit 아닌 버튼을 넣고싶을 때는
1. input type= button 으로 넣어줘야한다.
2. 또는 input type= button 으로 넣고 preventDefault() 해줘야한다
<p contenteditable="true">dd</p>
<script>
document.addEventListener('DOMContentLoaded', () => {
// 조작!
const img = document.querySelector('img');
// 값을 넣는다
img.setAttribute('src','http://placekitten.com/200/200')
// 값을 추출한다
console.log(img.getAttribute('src'));
})
</script>
이렇게 쓰거나 또는
<script>
document.addEventListener('DOMContentLoaded', () => {
// 조작!
const img = document.querySelector('img');
// 값을 넣는다
img.src = 'http://placekitten.com/200/200';
// 값을 추출한다
console.log(img.src);
})
</script>
이렇게 표준에 있는 속성은 img.src로 바로 활용할 수 있다.
표준에 없는 속성들은 무조건 setAttribute
와 getAttribute
를 활용해야 한다
change 이벤트 : 해당 input 태그 전체의 입력을 마쳤을 때 (enter를 쳤을때나 입력양식을 떠났을 때)
let [seconds, timerID] = [0, 1]
이렇게 쓰게 되면 seconds 에는 0, timerID 에는 1이 들어가게 됨.
저렇게 쓰는걸 다중할당연산 이라고 부름
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
const addTodo = () => {
if (input.value !== '') {
const div = document.createElement('div')
document.body.appendChild(div)
const checkbox = document.createElement('input')
checkbox.type = 'checkbox'
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
div.style.textDecoration = 'line-through'
} else {
div.style.textDecoration = ''
}
})
div.appendChild(checkbox)
const span = document.createElement('span')
span.textContent = input.value
input.value = ''
div.appendChild(span)
const deleteButton = document.createElement('button')
deleteButton.textContent = '제거하기'
deleteButton.addEventListener('click', () => {
div.parentNode.removeChild(div)
})
div.appendChild(deleteButton)
}
}
const h1 = document.createElement('h1')
h1.textContent = '할 일 목록'
document.body.appendChild(h1)
const input = document.createElement('input')
input.addEventListener('keyup', (event) => {
if (event.keyCode === 13) {
addTodo()
}
})
document.body.appendChild(input)
const addButton = document.createElement('button')
addButton.textContent = '추가하기'
document.body.appendChild(addButton)
addButton.addEventListener('click', () => {
if (input.value !== '') {
addTodo()
}
})
})
</script>
</head>
<body>
</body>
</html>