이 글은 '이웅모'님의 '모던 자바스크립트 Deep Dive' 책을 통해 공부한 내용을 정리한 글입니다. 저작권 보호를 위해 책의 내용은 요약되었습니다.
이벤트(event)란 어떠한 사건을 의미하며, 이벤트가 발생하는 시점이나 순서를 사전에 인지할 수 없으므로 일반적인 제어 흐름과는 다른 방식이 필요하다.
브라우저는 이벤트를 감지할 수 있으며 이벤트 발생 시 이를 통지해준다. 보통의 경우는 이벤트가 발생하기 전에는 실행되지 않다가 이벤트가 발생하면 실행되는 함수를 연결하며 이를 이벤트 핸들러라 한다.
브라우저는 단일 쓰레드(single-thread)에서 이벤트 드리븐(event-driven) 방식으로 동작한다. 웹 애플리케이션은 단일 쓰레드인 것에 비해 동시에 여러 개의 task가 처리되는 것처럼 보이는데, 이를 동시성(Concurrency)라고 하며 이를 지원하는 것이 바로 이벤트 루프이다.
위와 같은 특성 때문에 다음 예제 코드는 다음과 같은 결과를 나타낸다.
function a() {
console.log('a');
b();
}
function b() {
setTimeout(() => {
console.log('b');
}, 0);
c();
}
function c() {
console.log('c');
}
a(); // a -> c -> b
대표적인 이벤트 몇 가지는 다음과 같다. 추가 내용은 여기(MDN)서 확인해보자.
select box
, checkbox
, radio button
의 상태가 변경 시3가지 방식이 있다.
HTML 요소의 이벤트 핸들러 어트리뷰트에 이벤트 핸들러를 등록하는 방식이다. 더 이상 사용되지 않는다. on
으로 시작하는 이벤트 어트리뷰트의 값으로 함수 호출을 전달한다.
<button onclick="click()">Click</button>
<script>
function click() {
alert('Clicked!');
}
</script>
이벤트 어트리뷰트의 값으로 전달한 함수 호출은 런타임 때 즉시 실행되어 평가된 값이 전달되는 것은 아니다. 이벤트 어트리뷰트 키를 이름으로 갖는 함수를 암묵적으로 정의하고 함수의 몸체에 이벤트 어트리뷰트의 값으로 전달한 함수 호출을 문으로 갖는다. 즉, 다음과 같다.
function onclick(event) {
click();
// 추가로 할당 가능
}
따라서 여러 개의 문을 전달할 수 있다.
<button onclick="click(); dbclick()">Click</button>
<script>
function click() {
alert('Clicked!');
}
function dbclick() {
alert('Double Clicked!');
}
</script>
인라인 이벤트 핸들러 방식처럼 HTML과 Javascript가 뒤섞이는 문제는 해결할 수 있는 방식이다. 하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만을 바인딩할 수 있다는 단점이 있다.
<button>Click</button>
<script>
const btn = document.querySelector('button');
btn.onclick = function () {
alert('Button Clicked!');
};
</script>
대상 DOM 요소에 이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행될 콜백 함수(이벤트 핸들러)를 지정한다.
<button>Click</button>
<script>
const btn = document.querySelector('button');
btn.addEventListener('click', () => click());
function click() {
alert('Clicked!');
}
</script>
계층적 구조에 포함되어 있는 HTML 요소에 이벤트가 발생할 경우 연쇄적인 반응이 일어난다. 이를 이벤트 전파(Event Propagation)이라 한다. 전파 방향에 따라 캡처링과 버블링으로 구분된다.
addEventListener
메서드의 세번째 매개변수에 true
를 설정하면 캡처링으로 전파되는 이벤트를 캐치하고 false
또는 미설정시 버블링으로 전파되는 이벤트를 캐치한다.
이와 같은 이벤트 전파를 차단하기 위해서는 Target 부분에 event.stopPropagation()
메서드를 사용하면 된다.
document.querySelector('button').addEventListener('click', function(event) {
event.stopPropagation();
});
stopPropagation
메서드와 비슷하게 요소의 기본 동작을 방지하는 메서드이다. 폼을 submit하거나 링크를 클릭하면 다른 페이지로 이동하게 된다. 이와 같이 요소가 가지고 있는 기본 동작을 중단시키기 위한 메소드가 preventDefault()
이다.
document.querySelector('form').addEventListener('submit', function(event) {
event.preventDefault();
});
이벤트가 발생하면 event
객체는 동적으로 생성되며 이벤트를 처리할 수 있는 이벤트 핸들러에 인자로 전달된다.
const btn = document.querySelector('button').addEventListener('click', (event) => {
console.log(event);
});
위와 같이 event
객체에는 많은 정보를 담고 있다. (위 사진은 중략되었음)
event
객체는 이벤트 핸들러에 암묵적으로 전달된다. 그러나 이벤트 핸들러를 선언할 때, event 객체를 전달받을 첫 번째 매개변수를 명시적으로 선언하여야 한다.
대표적인 몇 가지 프로퍼티를 알아보자.
이벤트를 발생시킨 요소를 가리킨다.
이벤트에 바인딩된 DOM 요소를 가리킨다.
<div style="width:100px; height:100px; background-color:red">
<button>click</button>
</div>
const div = document.querySelector('div').addEventListener('click', clickbtn);
const btn = document.querySelector('button').addEventListener('click', clickbtn);
function clickbtn(event) {
console.log(this);
console.log(event.target);
console.log(event.currentTarget);
}
button
을 클릭하면 다음과 같은 결과가 나온다.
콜백함수에서 화살표 함수를 쓰지 않는다면 this === currentTarget
이 성립한다.
이벤트 위임(Event Delegation)은 다수의 자식 요소에 각각 이벤트 핸들러를 바인딩하는 대신 하나의 부모 요소에 이벤트 핸들러를 바인딩하는 방법이다. 이는 이벤트가 이벤트 흐름에 의해 이벤트를 발생시킨 요소의 부모 요소에도 영향(버블링)을 미치기 때문에 가능한 것이다.
<div style="width:100px; height:100px; background-color:red">
<button id="b1">click</button>
<button id="b2">click</button>
<button id="b3">click</button>
<button id="b4">click</button>
</div>
const div = document.querySelector('div').addEventListener('click', clickbtn);
function clickbtn(event) {
console.log(event.target);
}
버튼을 각각 눌러보면 다음과 같은 결과가 나온다.