이벤트
예를 들어 클릭했을 때 페이지가 넘어가는 등 이벤트가 발생하면 그에 맞는 반응을 해야 한다.
이를 위해 이벤트는 일반적으로 함수에 연결되며 그 함수는 이벤트가 발생하기 전에는 실행되지 않다가 이벤트가 발생되면 실행된다.
-> 이 함수를 이벤트 핸들러라고 한다.
이벤트 루프(Event Loop)
브라우저는 단일 스레드(single-thread)에서 이벤트 드리븐(event-driven) 방식으로 동작한다.
단일 스레드 : 스레드가 하나뿐이라는 의미, 곧 하나의 작업만을 처리할 수 있다
하지만 실제로 동작하는 웹 애플리케이션은 많은 task가 동시에 처리되는 것처럼 느껴진다.
이처럼 자바스크립트의 동시성(Concurrency)을 지원하는 것이 바로 이벤트 루프(Event Loop)다.
주요하게 생각할 점 call stack이 비어야 Callback queue(혹은 stack queue, event queue 다양하게 부른다)에 있는 함수가 콜스택으로 호출된다.
이벤트 핸들러
이벤트에 대응하는 처리하는 기술
이벤트 핸들러 등록 방법 3가지
- 인라인 이벤트 핸들러 방식
- 이벤트 핸들러 프로퍼티 방식
- addEventListener 메소드 방식 ✰✰✰
인라인 이벤트 핸들러 방식
HTML 요소의 이벤트 핸들러 어트리뷰트에 이벤트 핸들러를 등록하는 방법
✔︎ this는 전역 객체 window를 가리킨다
<!DOCTYPE html>
<html>
<body>
// HTML 요소에 등록
<button onclick="myHandler()">Click me</button>
// 여러 개 전달 가능
<!-- <button onclick="myHandler1(); myHandler2();">Click me</button> -->
<script>
function myHandler() {
alert('Button clicked!');
}
/*
function myHandler1() {
alert('myHandler1');
}
function myHandler2() {
alert('myHandler2');
}
*/
</script>
</body>
</html>
onclick 같이 on으로 시작하는 이벤트 어트리뷰트의 값으로 함수 호출을 전달
이때 이벤트 어트리뷰트의 값으로 전달한 함수 호출이 즉시 호출되는 것은 아니다.
하지만 이 방식은 사용하지 않아야 한다.
-> HTML과 JavaScript는 관심사가 다르므로 분리하는 것이 좋음
이벤트 핸들러 프로퍼티 방식
인라인 이벤트 핸들러 방식같이 HTML과 JavaScript가 뒤섞이는 문제를 해결할 수 있는 방식
하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만 바인딩할 수 있다.
✔︎ this는 이벤트에 바인딩된 요소를 가리킨다 (이벤트 객체의 currentTarget 프로퍼티와 동일)
<!DOCTYPE html>
<html>
<body>
<button class="btn">Click me</button>
<script>
const btn = document.querySelector('.btn');
// 이벤트 핸들러 프로퍼티 방식은 이벤트에 하나의 이벤트 핸들러만을 바인딩할 수 있다
// 첫번째 바인딩된 이벤트 핸들러 => 실행되지 않는다.
btn.onclick = function () {
alert('① Button clicked 1');
};
// 두번째 바인딩된 이벤트 핸들러
// 실행됨
btn.onclick = function () {
alert('① Button clicked 2'); // 첫 번째 실행
};
// addEventListener 메소드 방식
// 첫번째 바인딩된 이벤트 핸들러
btn.addEventListener('click', function () {
alert('② Button clicked 1'); // 두 번째 실행
});
// 두번째 바인딩된 이벤트 핸들러
btn.addEventListener('click', function () {
alert('② Button clicked 2'); // 세 번째 실행
});
</script>
</body>
</html>
addEventListener 메소드 방식
addEventListener 메소드를 이용하여 대상 DOM 요소에 이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행될 콜백 함수(이벤트 핸들러)를 지정한다.
✔︎ this는 이벤트 리스너에 바인딩된 요소(currentTarget)를 가리킨다 (이벤트 객체의 currentTarget 프로퍼티와 동일)
장점
- 하나의 이벤트에 대해 하나 이상의 이벤트 핸들러를 추가할 수 있다.
- 캡처링과 버블링을 지원
- HTML 요소뿐만아니라 모든 DOM 요소(HTML, XML, SVG)에 대해 동작한다.
브라우저는 웹 문서(HTML, XML, SVG)를 로드한 후, 파싱하여 DOM을 생성한다.
<!DOCTYPE html>
<html>
<body>
<script>
// 대상 DOM 요소를 지정하지 않으면 전역객체에서 발생하는 click이벤트에 이벤트 핸들러를 바인딩
// 어딜 클릭해도 이벤트 핸들러가 동작
addEventListener('click', function () {
alert('Clicked!');
});
</script>
</body>
</html>
이벤트의 흐름
계층적 구조에 포함되어 있는 HTML 요소에 이벤트가 발생할 경우 연쇄적 반응이 일어난다.
이벤트 전파 방향에 따라 버블링과 캡처링으로 구분할 수 있다.
- 버블링 : 자식 요소에서 발생한 이벤트가 부모 요소로 전파되는 것
- 캡처링 : 자식 요소에서 발생한 이벤트가 부모 요소부터 시작하여 이벤트를 발생시킨 자식 요소까지 도달하는 것
- 주의점❗️ 버블링과 캡처링은 둘 중에 하나만 발생하는 것이 아니라 캡처링부터 시작하여 버블링으로 종료한다
예시로 살펴보자
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
body area
<main>
main area
<div>
div area
<p>
p area
<span> span area </span>
</p>
</div>
</main>
<script>
const $body = document.querySelector("body");
const $main = document.querySelector("main");
const $div = document.querySelector("div");
const $p = document.querySelector("p");
const $span = document.querySelector("span");
//bubbling
$span.addEventListener("click", function () {
console.log(`span 태그`);
});
$p.addEventListener("click", function () {
console.log(`p 태그`);
});
$div.addEventListener("click", function () {
console.log(`div 태그`);
});
$main.addEventListener("click", function () {
console.log(`main 태그`);
});
$body.addEventListener("click", function () {
console.log(`body 태그`);
});
</script>
</body>
</html>
span 태그를 클릭했음에도 버블링으로 올라가는 것을 확인할 수 있다.
addEventListener는 기본적으로 버블링으로 동작
캡처링이 되도록 작성하려면 어떻게 해야할까?
-> addEventListener 3번째 인자를 true로 주면 캡처링(default는 false이고 버블링을 의미한다)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
body area
<main>
main area
<div>
div area
<p>
p area
<span> span area </span>
</p>
</div>
</main>
<script>
const $body = document.querySelector("body");
const $main = document.querySelector("main");
const $div = document.querySelector("div");
const $p = document.querySelector("p");
const $span = document.querySelector("span");
//capturing
$span.addEventListener("click", function () {
console.log(`capturing span 태그`);
},true);
$p.addEventListener("click", function () {
console.log(`capturing p 태그`);
},true);
$div.addEventListener("click", function () {
console.log(`capturing div 태그`);
},true);
$main.addEventListener("click", function () {
console.log(`capturing main 태그`);
},true);
$body.addEventListener("click", function () {
console.log(`capturing body 태그`);
},true);
//bubbling
$span.addEventListener("click", function () {
// event.stopPropagation();
console.log(`span 태그`);
});
$p.addEventListener("click", function () {
console.log(`p 태그`);
});
$div.addEventListener("click", function () {
console.log(`div 태그`);
});
$main.addEventListener("click", function () {
console.log(`main 태그`);
});
$body.addEventListener("click", function () {
console.log(`body 태그`);
});
</script>
</body>
</html>
span을 클릭했을 때 캡쳐링이 일어나는 것을 확인할 수 있다.
Event 객체
event 객체는 이벤트를 발생시킨 요소와 발생한 이벤트에 대한 유용한 정보를 제공한다.
이벤트가 발생하면 event 객체는 동적으로 생성되며 이벤트를 처리할 수 있는 이벤트 핸들러에 인자로 전달된다.
이벤트 핸들러를 선언할 때, event 객체를 전달받을 첫번째 매개변수를 명시적으로 선언해야 한다.
Event Property
✔︎ Event.target
=> 실제로 이벤트를 발생시킨 요소를 가리킨다
✔︎ Event.currentTarget
=> 이벤트에 바인딩된 DOM 요소를 가리킨다. (즉, addEventListener 앞에 기술된 객체를 의미)
✔︎ Event.type
=> 발생한 이벤트의 종류를 나타내는 문자열을 반환한다.
✔︎ Event.cancelable
=> 요소의 기본 동작을 취소시킬 수 있는지 여부(true / false)를 나타낸다.
✔︎ Event.eventPhase
=> 이벤트 흐름(event flow) 상에서 어느 단계(event phase)에 있는지를 반환한다.
반환값 | 의미 |
0 | 이벤트 없음 |
1 | 캡쳐링 단계 |
2 | 타깃 |
3 | 버블링 단계 |
기본 동작의 변경
이벤트 객체는 요소의 기본 동작과 요소의 부모 요소들이 이벤트에 대응하는 방법을 변경하기 위한 메소드를 가지고 있다.
✔︎ Event.preventDefault()
=> 요소가 가지고 있는 기본 동작을 중단시키기 위한 메소드
✔︎ Event.stopPropagation()
=> 어느 한 요소를 이용하여 이벤트를 처리한 후 이벤트가 부모 요소로 이벤트가 전파되는 것을 중단시키기 위한 메소드
=> 부모 요소에 동일한 이벤트에 대한 다른 핸들러가 지정되어 있을 경우 사용된다.
'항해 > TIL' 카테고리의 다른 글
TIL(2/23) / HTTP (0) | 2024.02.23 |
---|---|
TIL(2/19) / 프로토타입 (0) | 2024.02.19 |
TIL(2/15) / 객체, 변경불가성, 빌트인 객체 (0) | 2024.02.15 |
TIL(2/13) / JS 메서드 (0) | 2024.02.13 |
TIL(2/10) / DOM, 클래스, 클로저 (0) | 2024.02.10 |