목차
이벤트 청취 대상
버튼을 클릭하면 배경 색상이 바뀌는 이벤트 리스너를 만들어 보자. 지금까지 배운 대로라면 아래처럼 작성했을 것이다.
const button = document.querySelector('button')
button.addEventListener('click', () => {
button.style.setProperty('background-color', '#e54d26')
})
클릭(click) 이벤트를 리스닝(listening, 수신)하는 요소는 버튼(button)이다. 이벤트 리스너의 콜백(callback)을 별도로 분리해서 생성해도 코드는 정상적으로 작동한다.
function handleChangeBgColor () {
button.style.setProperty('background-color', '#e54d26')
}
const button = document.querySelector('button')
button.addEventListener('click', handleChangeBgColor)
하지만 콜백을 분리한 후 변수 이름을 변경하게 된다면 문제가 발생할 것이다. 문제가 발생하지 않게 하려면 이벤트 리스너의 콜백 안에서 button 변수 대신, 청취 요소를 가져와야 한다. 방법은 2가지다.
- this 키워드
- 이벤트가 발생한 대상(Event.currentTarget)
this 키워드
this는 JavaScript의 키워드로, this가 가리키는 대상은 함수를 호출하는 방식에 따라 달라진다. 일반 함수를 이벤트 리스너의 콜백으로 사용할 때는 청취 요소를 가리킨다. 단, 화살표 함수를 사용할 때는 this 키워드가 가리키는 대상이 이벤트 청취 요소가 아니기 때문에 주의해야 한다.
const button = document.querySelector('button')
// 화살표 함수 표현식에 this 사용
const handleArrowFunctionExpression = () => {
console.log(this)
}
// 화살표 함수 표현식에서의 this는 Window를 가리킨다
button.addEventListener('click', handleArrowFunctionExpression) // this === Window {}
// 일반 함수에서의 this는 <button>을 가리킨다
button.addEventListener('click', handleChangeBgColor) // this === <button>
// 일반 함수에 this 사용
function handleChangeBgColor () {
console.log(this)
}
이벤트가 발생한 대상(Event.currentTarget)
모든 이벤트 리스너의 콜백은 이벤트 객체(Event Object)를 인수로 전달 받는다. (일반적으로 이벤트 객체는 e 또는 ev, evt 등으로 줄여서 사용한다.) 이벤트 객체는 트리거(trigger)된 이벤트에 대한 정보를 제공한다. 이벤트 객체를 콘솔 패널에 출력하면 해당 정보를 확인할 수 있다.
button.addEventListener('click', (e) => {
console.log(e) // e === MouseEvent {}
})
이벤트 리스너 콜백에서 이벤트 객체의 currentTarget 속성은 이벤트 청취 요소를 가리킨다.
const button = document.querySelector('button')
button.addEventListener('click', (e) => {
console.log(e.currentTarget) // e.currentTarget === <button>
})
이벤트 리스너를 분리하든지, 인라인으로 바로 연결해 사용하든지 변수나 this 대신 리스너 함수에 인수로 전달되는 이벤트 객체를 활용하는 것을 권장한다.
이벤트 기본 작동 방지
요소들은 기본(default)으로 갖고 있는 작동 방식이 있다. 예를 들어, href 속성을 포함하는 링크를 클릭하면 브라우저는 해당 주소로 이동하거나, 체크박스를 클릭하면 브라우저가 "선택됨" 또는 "선택 해제됨" 상태로 전환하는 등 다양한 방식으로 기본 작동한다. 이러한 기본 작동을 방지해야 할 때도 있다. 브라우저의 기본 작동을 방지하려면 이벤트 객체의 preventDefault() 메서드를 실행해야 한다. 기본 작동이 방지되면 브라우저는 이벤트 객체의 defaultPrevented 속성을 true로 설정한다.
요소.addEventListener('click', (e) => {
// 브라우저의 기본 작동 방지
e.preventDefault()
console.log(e.defaultPrevented) // true
})
오늘 하루를 돌아보며
수업 시간의 절반 이상을 캐러셀 만들기 실습을 같이 해보는 데 할애했다. 어제 수업 끝나고 혼자 해봤는데 오늘 강사님과 같이 해보면서 배운 내용을 적용하는 일이 쉽지 않다는 것을 깨달았다. 어제 혼자 해볼 때는 정말 머리 쥐어짜면서 어떻게 어떻게 해내긴 했는데, 오늘 강사님이 풀어나가는 방식을 보면서 배웠던 내용을 배웠다는 사실조차 잊고 있었다는 것을 알았다. 다음 요소를 선택하는 방법이나 계산된 스타일을 불러오는 방법 등 분명 어제 다 배운 건데 배운 내용을 그대로 사용할 생각은 못 했다는 게 조금 부끄러웠다. 그래도 직접 문제에 부딛히고, 해결해 보면서 애써봤으니까 오늘 강사님의 설명을 충분히 이해할 수 있었다!
수업 마지막에는 훈련 평가 퀴즈를 풀었다. 말은 거창해도 10개의 객관식 문제가 다였다. 생각보다 할 만 하다고 생각했는데 마지막 문제에서 뇌가 생각을 멈췄다. 도저히 답을 알 수 없었다. 그래서 그냥 답이 없다고 찍었는데...
// 다음 코드에서 발생할 수 있는 문제점으로 가장 적절한 것은?
window.addEventListener('load', () => {
const buttons = document.getElementsByClassName('button')
for (var i = 0, l = buttons.length; i < l; i++) {
buttons[i].addEventListener('click', function() {
console.log('버튼 ' + i + '번이 클릭되었습니다.')
})
}
})
// 답: 어느 버튼을 클릭해도 모두 같은 인덱스 번호를 출력한다.
변수를 선언할 때 var를 사용한 것을 못 봤다. var는 함수 스코프를 가지기 때문에 for 문 밖에서도 i에 접근할 수 있다. for 문이 끝나도 i의 최종 값이 유지되는 것이다. 그러면 버튼을 클릭할 때마다 console.log()를 실행하는데, 여기에서 i의 값으로 for 문이 끝난 후의 최종 값을 출력하게 된다. 버튼이 2개면 모든 버튼이 '버튼 2번이 클릭되었습니다.'라고 출력하고, 4개면 '버튼 2번이 클릭되었습니다.'라고 출력하는 것이다. for 문 안에 var가 아닌 let으로 변수를 선언해야 한다.