버튼을 클릭하면 무슨 일이 일어날까요?
onclick부터 addEventListener, 이벤트 위임까지 — 클릭 이벤트의 모든 것을 실습과 함께 마스터해요.
사용자의 행동(클릭, 키 입력, 스크롤 등)을 JavaScript가 감지하는 방법
웹 페이지는 단순히 보는 것에서 끝나지 않아요. 사용자가 버튼을 클릭하거나, 폼에 글자를 입력하거나, 마우스를 올리는 순간 — 이 모든 행동이 브라우저에서 이벤트(Event)로 발생해요. JavaScript는 이 이벤트를 감지해서 원하는 동작을 실행할 수 있어요.
가장 흔하게 쓰이는 이벤트가 바로 클릭 이벤트(click event)예요. 사용자가 마우스로 요소를 클릭하거나, 터치스크린에서 탭(tap)하는 순간 발생해요. 쇼핑몰의 "장바구니 담기" 버튼, 모달창 열기 버튼, 메뉴 토글 버튼 — 모두 클릭 이벤트로 동작해요.
브라우저에서 발생하는 모든 사건이에요. click, keydown, mouseover, submit 등 수십 가지 종류가 있어요.
이벤트가 발생했을 때 실행할 함수예요. "이 요소에서 click이 발생하면 이 함수를 실행해" 라고 등록해두는 거예요.
이벤트가 실제로 발생한 요소예요. event.target으로 접근할 수 있어요. 어떤 버튼을 클릭했는지 알 수 있어요.
HTML로 만들어진 DOM 요소에 이벤트 리스너를 붙여요. DOM을 먼저 불러온 뒤 JavaScript를 실행해야 정상 동작해요.
DOM이란? Document Object Model의 약자로, HTML 문서를 JavaScript가 다룰 수 있는 객체 구조로 표현한 것이에요. document.getElementById()로 요소를 찾고 그 위에 이벤트를 달아요.
사용자가 요소를 클릭하면, 브라우저는 다음 순서로 처리해요.
| 단계 | 설명 | 관련 개념 |
|---|---|---|
| ① 클릭 발생 | 사용자가 마우스 버튼을 누르고 뗌 | mousedown + mouseup |
| ② 이벤트 객체 생성 | 브라우저가 click 이벤트 객체를 만들어 세부 정보 담음 | Event 객체 |
| ③ 캡처링 단계 | document → html → body → ... → 클릭된 요소로 이벤트 내려감 | 캡처링 |
| ④ 타겟 단계 | 클릭된 요소에 등록된 리스너 실행 | 타겟 |
| ⑤ 버블링 단계 | 클릭된 요소 → 부모 → ... → document 로 이벤트 올라감 | 버블링 |
주의: <script> 태그를 </body> 바로 앞에 놓거나, DOMContentLoaded 이벤트 안에서 실행해야 해요. HTML 요소가 생성되기 전에 JavaScript가 실행되면 요소를 찾지 못해서 에러가 나요.
HTML 속성, JS 프로퍼티, addEventListener — 상황에 맞는 방법을 골라 써요
onclick="..." 속성으로 이벤트를 붙이는 방법이에요. 빠르게 테스트할 때 편리해요.onclick 프로퍼티에 함수를 할당하는 방법이에요. HTML과 JS를 분리할 수 있어요.removeEventListener)도 가능해요./* ① HTML onclick 속성 */ <button onclick="alert('클릭!')">클릭</button> /* ② JS onclick 프로퍼티 */ <button id="btn">클릭</button> <script> const btn = document.getElementById('btn'); btn.onclick = function() { alert('클릭!'); }; </script> /* ③ addEventListener — 권장 방법 */ <button id="btn2">클릭</button> <script> const btn2 = document.getElementById('btn2'); btn2.addEventListener('click', function() { alert('클릭!'); }); </script>
AI에게 코드를 짜달라고 할 때 "addEventListener를 사용해서 버튼 클릭 이벤트를 추가해줘"라고 명시하면 현대적인 코드를 받을 수 있어요. 작은 인라인 핸들러가 필요하다면 "onclick 속성으로" 라고 요청하면 돼요.
복붙하면 바로 쓸 수 있는 4가지 핵심 패턴이에요
가장 기본적인 버튼 클릭 이벤트 처리 방법이에요.
<button id="myBtn">클릭해보세요</button> <p id="result"></p> <script> const btn = document.getElementById('myBtn'); const result = document.getElementById('result'); btn.addEventListener('click', function(event) { // event 객체에는 클릭 좌표, 타겟 등 정보가 담겨있어요 result.textContent = `클릭! X: ${event.clientX}, Y: ${event.clientY}`; }); </script>
다크모드 전환, 메뉴 열기/닫기, 좋아요 버튼 등에 활용해요.
<button id="toggleBtn">🌙 다크모드 켜기</button> <script> const btn = document.getElementById('toggleBtn'); let isDark = false; btn.addEventListener('click', () => { isDark = !isDark; // true/false 반전 if (isDark) { document.body.style.background = '#111'; document.body.style.color = '#fff'; btn.textContent = '☀️ 라이트모드 켜기'; } else { document.body.style.background = '#fff'; document.body.style.color = '#111'; btn.textContent = '🌙 다크모드 켜기'; } }); </script>
동적으로 추가되는 요소들을 효율적으로 처리하는 핵심 패턴이에요.
<ul id="list"> <li><button class="del-btn">삭제</button> 항목 1</li> <li><button class="del-btn">삭제</button> 항목 2</li> </ul> <script> const list = document.getElementById('list'); // 각 li마다 이벤트를 달지 않고, 부모 ul에 하나만 달아요 list.addEventListener('click', function(event) { if (event.target.classList.contains('del-btn')) { // 클릭된 것이 삭제 버튼이면, 부모 li 제거 event.target.closest('li').remove(); } }); // 나중에 동적으로 추가해도 자동으로 이벤트 적용돼요! function addItem(text) { const li = document.createElement('li'); li.innerHTML = `<button class="del-btn">삭제</button> ${text}`; list.appendChild(li); } </script>
기본 동작(폼 제출, 링크 이동 등)을 막고 커스텀 처리하는 방법이에요.
<form id="myForm"> <input type="text" id="nameInput" placeholder="이름 입력"> <button type="submit">제출</button> </form> <p id="msg"></p> <script> const form = document.getElementById('myForm'); form.addEventListener('submit', function(event) { event.preventDefault(); // 기본 폼 제출(페이지 새로고침) 막기 const name = document.getElementById('nameInput').value; if (!name.trim()) { document.getElementById('msg').textContent = '이름을 입력해주세요!'; return; } document.getElementById('msg').textContent = `${name}님, 제출 완료!`; }); </script>
직접 클릭해보며 각 패턴이 어떻게 동작하는지 체험해보세요
부모 컨테이너 하나가 모든 버튼 클릭을 처리해요
클릭 이벤트를 배우면서 자주 헷갈리는 것들을 모았어요
간단한 프로토타입이나 1회성 테스트라면 onclick이 빠르고 간편해요. 하지만 실제 프로젝트에서는 addEventListener를 쓰는 것을 강력히 권장해요.
이유는 세 가지예요. 첫째, onclick은 하나의 이벤트 핸들러만 등록할 수 있어서 나중에 또 다른 onclick을 붙이면 이전 것이 사라져요. addEventListener는 여러 개를 등록해도 모두 실행돼요. 둘째, removeEventListener로 나중에 이벤트를 깔끔하게 제거할 수 있어요. 셋째, 캡처링 단계를 사용하고 싶을 때도 addEventListener만 가능해요.
이벤트 버블링(Event Bubbling)은 자식 요소에서 발생한 이벤트가 부모 → 조상 요소로 순서대로 전파되는 현상이에요. 마치 물속에서 거품(bubble)이 위로 떠오르는 것처럼요.
예를 들어 <div> 안의 <button>을 클릭하면, button의 click 이벤트가 발생한 뒤 div의 click 이벤트도 순서대로 발생해요. 이 특성을 활용한 것이 이벤트 위임(Event Delegation)이에요. 부모에 이벤트 하나만 달아서 여러 자식의 이벤트를 한꺼번에 처리할 수 있어요.
event.stopPropagation()은 이벤트가 부모 요소로 버블링되는 것을 막아요. 자식 요소의 이벤트 처리가 부모 이벤트에 영향을 주지 않게 하고 싶을 때 써요.
가장 흔한 사례는 모달창이에요. 모달 배경(오버레이)을 클릭하면 모달이 닫히는데, 모달 내부 콘텐츠를 클릭할 때는 닫히면 안 되잖아요. 이때 모달 내부에 event.stopPropagation()을 추가하면 배경 클릭 이벤트로 전파되지 않아요. 단, 남발하면 디버깅이 어려워지니 꼭 필요한 경우에만 써요.
JavaScript로 나중에 document.createElement()로 만든 요소에는 기존에 등록한 이벤트 리스너가 자동으로 붙지 않아요. 해결 방법은 두 가지예요.
방법 1 — 요소 생성 직후 이벤트 달기: 요소를 만들고 DOM에 추가하기 전에 element.addEventListener()로 이벤트를 달아요. 요소별로 이벤트를 달아야 해서 요소가 많으면 메모리를 많이 써요.
방법 2 — 이벤트 위임(권장): 동적 요소의 공통 조상(부모) 요소에 이벤트를 달고, 이벤트가 발생했을 때 event.target으로 어떤 자식이 클릭됐는지 확인해요. 요소가 동적으로 추가/제거돼도 자동으로 작동하고 메모리 효율도 좋아요.
네! addEventListener의 첫 번째 인자에 이벤트 타입을 바꾸면 돼요.
더블클릭: element.addEventListener('dblclick', handler) — 요소를 빠르게 두 번 클릭하면 발생해요.
우클릭(컨텍스트 메뉴): element.addEventListener('contextmenu', handler) — 마우스 오른쪽 버튼 클릭 시 발생해요. event.preventDefault()를 함께 쓰면 브라우저 기본 우클릭 메뉴를 막을 수 있어요. 커스텀 컨텍스트 메뉴를 만들 때 활용해요.
이 밖에도 mousedown, mouseup, mouseover, mouseout, mousemove 등 다양한 마우스 이벤트가 있어요.
클릭 이벤트를 마스터했다면, 자연스럽게 이어지는 주제예요