
Event
웹 페이지에서 마우스 클릭, 키 입력, 포커스 이동 등 어떤 사건을 발생시키는 것
이벤트 객체는 특정 이벤트가 발생했을 때, 해당 이벤트에 대한 상세 정보가 담긴 정보 꾸러미와 같다.
브라우저는 이벤트가 발생하면 이 정보 꾸러미(객체)를 자동으로 생성해서, 우리가 지정한 이벤트 리스너의 첫 번째 인자로 전달해 준다.
const myButton = document.getElementById("myButton");
myButton.addEventListener("click", (event) => {
console.log(event);
});
이 event 객체 안에는 "어떤 종류의 이벤트가", "어디서", "언제", "어떻게" 발생했는지 등 온갖 유용한 정보가 들어있다.
객체 안에는 수많은 정보가 있지만, 실제 개발에서 자주 쓰이는 것들은 아래와 같다.
event.target: 실제로 이벤트가 시작된 요소event.currentTarget: 이벤트 리스너가 현재 연결되어 있는 요소event.type: 발생한 이벤트의 종류 (String)event.key / event.code: 사용자가 누른 키에 대한 정보event.clientX / event.clientY: 브라우저 화면(Viewport) 기준으로 마우스 커서의 X, Y 좌표event.preventDefault(): 브라우저의 기본 동작을 막는 매우 중요한 메서드<a> 태그를 클릭했을 때 페이지 이동을 막고 다른 동작(e.g. 팝업 띄우기)을 시키고 싶을 때<form> 안에서 submit 버튼을 눌렀을 때, 페이지가 새로고침되는 것을 막고 HTTP 요청을 보내고 싶을 때const myLink = document.getElementById("myLink");
myLink.addEventListener("click", (event) => {
event.preventDefault();
alert("링크의 기본 동작을 막았습니다.");
});
event.stopPropagation(): 이벤트가 상위 요소로 전파(버블링)되는 것을 막음stopPropagation()을 사용해 이벤트 전파를 차단DOM Tree의 특정 요소에서 이벤트가 발생했을 때, 그 이벤트가 단순히 해당 요소에서 끝나지 않고 DOM 트리를 따라 다른 요소들에게 퍼져나가는 현상
이벤트 전파는 아래 두 가지 단계로 나뉜다.
비유하자면, 연못에 돌을 던졌을 때 돌이 수면까지 내려가는 과정이 캡처링, 돌이 닿은 지점부터 물결이 바깥으로 퍼져나가는 과정이 버블링과 같다.
window)부터 시작하여 이벤트가 발생한 실제 요소(target)까지 내려가는 과정target)부터 시작하여 다시 최상위 요소(window)까지 올라가는 과정addEventListener는 기본적으로 버블링 단계의 이벤트만 감지한다.{ capture: true } 옵션을 주면 캡처링 단계의 이벤트를 감지할 수 있다.
여러 자식 요소의 이벤트를 부모 요소 하나에서 처리하는 기법
버블링과
event.target속성을 활용하는 것이 핵심이다.
ul, ol)처럼 동적으로 아이템이 추가되거나 삭제될 때td)이나 버튼이 많은 UI처럼 비슷한 요소에 동일한 이벤트를 적용해야 할 때이벤트는 버블링이라는 특성을 가진다.
특정 요소에서 이벤트가 발생하면, 그 이벤트를 처리한 후 부모 요소로, 또 그 부모 요소로 올라가며 이벤트를 전파한다.
이벤트 위임은 바로 이 버블링을 활용하는 것이다.
부모 요소에 "너의 자식 중 누군가에게 이벤트가 생기면 나에게 알려줘"라고 말해두는 것과 같다.
<ul id="itemList">
<li>항목 1</li>
<li>항목 2</li>
<li>항목 3</li>
<li>항목 4</li>
</ul>
const itemList = document.getElementById("itemList");
// 부모 요소에 이벤트 리스너를 하나만 답니다.
itemList.addEventListener("click", (event) => {
// 클릭된 요소가 LI 태그가 맞는지 확인합니다.
if (event.target && event.target.nodeName === "LI") {
console.log("클릭된 항목:", event.target.textContent);
// event.target은 실제 클릭된 <li> 요소를 가리킵니다!
}
});
li나 a 태그 각각에 이벤트 리스너를 추가하는 대신, 부모인 ul에 단 하나만 추가한다.
event.target을 통해 어떤 메뉴가 클릭되었는지 정확히 알 수 있다.
여기서 버블링과 연결고리를 지어본다면 아래와 같다.
<li>)를 클릭한다. '항목 2'가 이벤트의 target이 된다.click 이벤트는 처리가 끝난 후, 이벤트 버블링 때문에 부모인 <ul>로 전파됨<ul>에 붙어 있던 이벤트 리스너가 자신의 자식 중 하나에서 클릭 이벤트가 버블링되어 올라왔다며 동작하기 시작event.target을 확인하면, 비록 리스너는 <ul>에 있지만 target은 최초의 이벤트 발생지인 '항목 2'(<li>)를 정확히 가리키고 있음결론적으로, 이벤트 버블링이라는 전파 현상이 없다면 자식 요소에서 발생한 이벤트가 부모 요소까지 전달될 수 없다.
그러면 이벤트 위임 패턴은 성립할 수 없게 된다.
연속적으로 발생하는 이벤트를 그룹화하여 특정 시간이 지난 후에 마지막 이벤트에 대해서만 콜백 함수를 실행하는 기법
<input type="text" id="searchBox" placeholder="검색어를 입력하세요..." />
const searchBox = document.getElementById("searchBox");
let debounceTimer; // 타이머 ID를 저장할 변수
function search(keyword) {
console.log(`API 요청 실행: ${keyword}`);
}
searchBox.addEventListener("input", function (event) {
// 1. 기존에 설정된 타이머가 있다면 취소합니다.
clearTimeout(debounceTimer);
const keyword = event.target.value;
// 2. 500ms(0.5초) 후에 search 함수를 실행하도록 새로운 타이머를 설정합니다.
// 만약 0.5초 안에 새로운 입력이 들어오면, 위에서 타이머가 취소되고 새 타이머가 설정됩니다.
debounceTimer = setTimeout(() => {
search(keyword);
}, 500);
});
이벤트를 일정 시간 간격으로 딱 한 번만 실행하도록 제한하는 기법
let throttleTimer; // 쓰로틀링 상태를 체크할 변수
function onScroll() {
console.log("스크롤 이벤트 발생! 하지만 쓰로틀링으로 조절됩니다.");
}
window.addEventListener("scroll", function () {
// 1. 현재 타이머가 설정되어 있다면(쓰로틀링 중이라면) 아무것도 하지 않고 함수를 종료합니다.
if (throttleTimer) {
return;
}
// 2. 타이머를 설정하고, 그 안에 실제 실행할 함수를 넣습니다.
// 이 함수는 즉시 실행됩니다.
throttleTimer = setTimeout(() => {
onScroll();
// 3. 200ms 후에 타이머를 null로 만들어 다음 이벤트가 실행될 수 있게 합니다.
throttleTimer = null;
}, 200);
});