- 화면을 스크롤하는 것
- 버튼을 클릭했을 때 팝업 창이 출력되는 것
- 마우스 커서의 위치에 따라 드래그 앤 드롭하는 것
- 사용자의 키보드 입력 값에 따라 새로운 요소를 생성하는 것
웹에서의 모든 동작은 이벤트 발생과 함께 한다.
무언가 일어났다는 신호, 사건
- 모든 DOM 요소는 이러한 event를 만들어 냄
- DOM에서 이벤트가 발생했을 때 생성되는 객체
- 이벤트 종류
- mouse, input, keyboard, touch …
DOM 요소에서 event가 발생하면, 해당 event는 연결된 이벤트 처리기(event handler)에 의해 처리 됨
특정 이벤트가 발생했을 때 실행되는 함수
- 사용자의 행동에 어떻게 반응할 지를 JavaScript 코드로 표현한 것
- addEventListener()
-
대표적인 이벤트 핸들러 중 하나
-
특정 이벤트를 DOM 요소가 수신할 때마다 콜백 함수를 호출
-
EventTarget(DOM 요소).addEventListener(type(수신할 이벤트), handler(콜백 함수))
- “대상에 특정 Event가 발생하면, 지정한 이벤트를 받아 할 일을 등록한다.”
element.addEventListener('click', function (event) { // 이벤트 처리 로직 })
- type
- 수신할 이벤트 이름
- 문자열로 작성 (ex. ‘click’)
- handler
- 발생한 이벤트 객체를 수신하는 콜백 함수
- 이벤트 핸들러는 자동으로 event 객체를 매개변수로 받음
-
-
“버튼을 클릭하면 버튼 요소 출력하기”
- 버튼에 이벤트 처리기를 부착하여 클릭 이벤트가 발생하면 이벤트가 발생한 버튼 정보를 출력
-
요소에 addEventListener를 연결하게 되면 내부의 this 값은 연결된 요소를 가리키게 됨 (event 객체의 currentTarget 속성 값과 동일)
<button id='btn'>버튼</button>
// 1. 버튼 선택 const btn = document.querySelector('#btn') // 2. 콜백 함수 const detectClick = function (event) { console.log(event) // PointerEvent console.log(event.currentTarget) // <button id="btn">버튼</button> console.log(this) // <button id='btn'>버튼</button> } // 3. 버튼에 이벤트 핸들러를 부착 btn.addEventListner('click', detectClick)
- 이벤트 핸들러 내부의 this는 이벤트 리스너에 연결된 요소(currentTarget)를 가리킴
- 이벤트가 발생하면 event 객체가 생성되어 첫 번째 인자로 전달
- event 객체가 필요 없는 경우 생략 가능
- 반환 값 없음
- “한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작하는 현상”
- 가장 최상단의 조상 요소(document)를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작
- 이벤트가 제일 깊은 곳에 있는 요소에서 시작해 부모 요소를 거슬러 올라가며 발생하는 것이 마치 물속 거품과 닮았기 때문
- 최하위의
요소를 클릭하면 p → div → form 순서로 3개의 이벤트 핸들러가 모두 순차적으로 동작했던 것
- ‘currentTarget’ 속성
- ‘현재’ 요소
- 항상 이벤트 핸들러가 연결된 요소만을 참조하는 속성
- ‘this’와 같음
- ‘target’ 속성
- 이벤트가 발생한 가장 안쪽의 요소(target)를 참조하는 속성
- 실제 이벤트가 시작된 요소
- 버블링이 진행 되어도 변하지 않음
- 핸들러는 outerouter에만 연결되어 있지만 하위 요소 outer와 inner를 클릭해도 해당 핸들러가 동작함
- 클릭 이벤트가 어디서 발생했든 상관없이 outerouter까지 이벤트가 버블링 되어 핸들러를 실행시키기 때문
- 이벤트가 하위 요소로 전파되는 단계 (버블링과 반대)
- table의 하위 요소 td를 클릭하면 이벤트는 먼저 최상위 요소부터 아래로 전파됨 (캡처링)
- 실제 이벤트가 발생한 지점(event.target)에서 실행된 후 다시 위로 전파 (버블링)
- 이 전파 과정에서 상위 요소에 할당된 이벤트 핸들러들이 호출되는 것
→ 캡처링은 실제 개발자가 다루는 경우가 거의 없으므로 버블링에 집중하기
-
만약 다음과 같이 각자 다른 동작을 수행하는 버튼이 여러 개가 있다고 가정
<div> <button></button> <button></button> ... <button></button> <button></button> </div>
-
그렇다면 각 버튼마다 서로 다른 이벤트 핸들러를 할당해야 할까?
→ 각 버튼의 공통 조상인 div 요소에 이벤트 핸들러 단 하나만 할당하기
-
요소의 공통 조상에 이벤트 핸들러를 단 하나만 할당하면, 여러 버튼 요소에서 발생하는 이벤트를 한꺼번에 다룰 수 있음
-
공통 조상에 할당한 핸들러에서 event.target을 이용하면 실제 어떤 버튼에서 이벤트가 발생했는지 알 수 있기 때문
// 예시 const divTag = document.querySelector('div') divTag.addEventListener('click', function (event) { console.log(event.target) })
-
console.log()로 event 객체를 출력할 경우 currentTarget 키의 값은 null을 가짐
-
currentTarget은 이벤트가 처리되는 동안에만 사용할 수 있기 때문
-
대신 console.log(event.currentTarget)을 사용하여 콘솔에서 확인 가능
→ currentTarget 이후의 속성 값들은 ‘target’을 참고해서 사용하기
- 버튼을 클릭하면 숫자를 1씩 증가해서 출력하기
- 사용자 입력 값을 실시간으로 출력하기
- 사용자 입력 값을 실시간으로 출력하기 + 버튼을 클릭하면 출력된 값의 CSS 스타일을 변경하기
- todo 프로그램 구현
- 로또 번호 생성기 구현
클릭횟수 : 2
<!-- click-event.html -->
<button id='btn'>버튼</button>
<p>클릭횟수 : <span id='counter'>0</span></p>// 1. 초기값 할당
let counterNumber = 0
// 2. 버튼 요소 선택
const btn = document.querySelector('#btn')
// 3. 콜백 함수 (버튼에 클릭 이벤트가 발생할 때 마다 실행할 코드)
const clickHandler = function () {
// 3.1 초기값 += 1
counterNumber += 1
// 3.2 p 요소를 선택
const spanTag = document.querySelector('#counter')
// 3.3 p 요소의 컨텐츠를 1 증가한 초기값으로 설정
spanTag.textContent = counterNumber
}
// 4. 버튼에 이벤트 핸들러 부착 (클릭 이벤트)
btn.addEventListener('click', clickHandler)<!-- input-event.html -->
<input type='text' id='text-input'>
<p></p>// 1. input 요소 선택
const inputTag = document.querySelector('#text-input')
// 2. p 요소 선택
const pTag = document.querySelector('p')
// 3. 콜백 함수 (input 요소에 input 이벤트가 발생할 때마다 실행할 코드)
const inputHandler = function (event) {
// 3.1 작성하는 데이터가 어디에 누적되고 있는지 찾기
// console.log(event.currentTarget)
console.log(event.currentTarget.value)
// 3.2 p요소의 컨텐츠에 작성하는 데이터를 추가
pTag.textContent = event.currentTarget.value
}
// 4. input 요소에 이벤트 핸들러 부착 (input 이벤트)
inputTag.addEventListener('input', inputHandler)- 사용자의 입력 값을 실시간으로 출력
- “+” 버튼을 클릭하면 출력한 값의 CSS 스타일을 변경하기
<!-- click-input-event.html -->
<head>
<style>
.blue {
color: blue;
}
</style>
</head>
<body>
<h1></h1>
<button id='btn'>클릭</button>
<input type='text' id='text-input'>
</body>// input 구현
const inputTag = document.querySelector('#text-input')
const h1Tag = document.querySelector('h1')
const inputHandler = function (event) {
h1Tag.textContent = event.currentTarget.value
}
inputTag.addEventListener('input', inputHandler)
// click 구현
const btn = document.querySelector('#btn')
const clickHandler = function () {
// 1. add 방법
h1Tag.classList.add('blue')
// 2. toggle 방법
h1Tag.classList.toggle('blue')
// 3. if 방법
if (h1Tag.classList.value) {
h1Tag.classList.remove('blue')
} else {
h1Tag.classList.add('blue')
}
}
btn.addEventListener('click', clickHandler)<!-- todo.html -->
<input type='text' class='input-text'>
<button id='btn'>+</button>
<ul></ul>// 1. 필요한 요소 선택
const inputTag = document.querySelector('.input-text')
const btn = document.querySelector('#btn')
const ulTag = document.querySelector('ul')
// 2. 콜백 함수 (실제 todo 데이터를 생성 후 추가하는 로직)
const addTodo = function (event) {
// 2.1 사용자 입력 데이터 저장
const inputData = inputTag.value
// 2.2 데이터를 저장할 li 요소를 생성
const liTag = document.createElement('li')
// 2.3 li 요소 컨텐츠에 데이터 입력
liTag.textContent = inputData
// 2.4 li 요소를 부모 ul 요소의 자식 요소로 추가
ulTag.appendChild(liTag)
// 2.5 todo 추가 후 input의 입력 데이터는 초기화
inputTag.value = ''
}
btn.addEventListener('click', addTodo)-
todo 추가 기능 구현
- 빈 문자열 입력 방지
- 입력이 없을 경우 경고 대화 상자를 띄움
<!-- todo.html --> const addTodo = function (event) { const inputData = inputTag.value if (inputData.trim()) { const liTag = document.createElement('li') liTag.textContent = inputData ulTag.appendChild(liTag) inputTag.value = '' } else { alert('할 일을 입력하세요..') } }
<!-- lottery.html -->
<h1>로또 추천 번호</h1>
<button id='btn'>행운 번호 받기</button>
<div></div><script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script>
// 1. 필요한 요소 선택
const h1Tag = document.querySelector('h1')
const btn = document.querySelector('#btn')
const divTag = document.querySelector('div')
// 2. 로또 번호를 생성하는 함수
const getLottery = function (event) {
// 2.1 1부터 45까지의 값이 필요
const numbers = _.range(1, 46)
// 2.2 45개의 요소가 있는 배열에서 6개 번호 추출
const sixNumbers = _.sampleSize(numbers, 6)
// 2.5 6개의 li 요소를 담을 ul 요소 생성
const ulTag = document.createElement('ul')
// 2.3 추출한 번호 배열을 "반복"하면서 li 요소를 생성
sixNumbers.forEach(number => {
// 2.4 번호를 담을 li 요소 생성 후 입력
const liTag = document.createElement('li')
liTag.textContent = number
// 2.6 만들어진 li를 ul 요소에 추가
ulTag.appendChild(liTag)
});
// 2.7 완성한 ul 요소를 div 요소에 추가
divTag.appendChild(ulTag)
}
// 3. 버튼 요소에 이벤트 핸들러를 부착
btn.addEventListener('click', getLottery)
</script>- lodash
- 모듈성, 성능 및 추가 기능을 제공하는 JavaScript 유틸리티 라이브러리
- array, object 등 자료구조를 다룰 때 사용하는 유용하고 간편한 함수들을 제공
- https://lodash.com/
- HTML의 각 요소가 기본적으로 가지고 있는 이벤트가 때로는 방해가 되는 경우가 있어 이벤트의 기본 동작을 취소할 필요가 있음
- 예시
- form 요소의 제출 이벤트를 취소하여 페이지 새로고침을 막을 수 있음
- a 요소를 클릭 할 때 페이지 이동을 막고 추가 로직을 수행할 수 있음
해당 이벤트에 대한 기본 동작을 실행하지 않도록 지정
- 콘텐츠를 복사 하는 것을 방지
<!-- prevent-event.html -->
<h1>중요한 내용</h1>
<script>
const h1Tag = document.querySelector('h1')
h1Tag.addEventListener('copy', function (event) {
console.log(event)
event.preventDefault()
alert('복사 할 수 없습니다.')
})
</script>- form 요소의 submit 동작 (action 값으로 요청)을 취소 시킴
<!-- prevent-event.html -->
<form id='my-form'>
<input type='text' name="username">
<button type="submit">Submit</button>
</form>const formTag = document.querySelector('#my-form')
const handleSubmit = function (event) {
event.preventDefault()
}
formTag.addEventListener('submit', handleSubmit)-
화살표 함수는 자신만의 this를 생성하지 않음
-
대신, 화살표 함수가 정의된 곳의 상위 스코프의 this를 그대로 사용
-
대부분의 경우, 이는 전역 객체(브라우저에서는 window)를 가리키게 됨
-
해결책
- 일반 함수로 사용하기
- 화살표 함수일 경우 event.currentTarget을 사용하기
element.addEventListener('click', function () { console.log(this) // <button id="function">function</button> }) element.addEventListener('click', () => { console.log(this) // window })