이벤트는 사용자가 일으키는 사건입니다. 클릭, 글자 입력, 폼 전송, 키 누름이 모두 이벤트입니다. 이 글은 “버튼이 반응하지 않는” 증상 다섯 가지를 진단하며 이벤트 문법과 버블링·위임을 익힙니다.
증상 1: 버튼을 눌러도 아무 일이 없습니다
정상 동작하는 기본형입니다. addEventListener의 인수는 사건의 이름과 그때 실행할 함수 두 개입니다.
<button id="saveBtn">글 저장</button>
<p id="status"></p>
<script>
const saveBtn = document.querySelector("#saveBtn");
const status = document.querySelector("#status");
saveBtn.addEventListener("click", function () {
status.textContent = "저장했습니다.";
});
</script>
반응이 없다면 원인은 대개 셋입니다. 선택자를 잘못 써서 saveBtn이 null이거나, 스크립트가 버튼보다 먼저 실행되었거나, 이벤트 이름의 철자가 틀린 경우입니다.
증상 2: 페이지를 열자마자 한 번 실행되고 끝납니다
가장 많이 나오는 실수입니다. 두 번째 인수에 함수 이름 대신 함수를 호출한 결과를 넣은 것입니다.
function saveNote() { status.textContent = "저장했습니다."; }
saveBtn.addEventListener("click", saveNote()); // 틀림 — 즉시 실행됩니다
saveBtn.addEventListener("click", saveNote); // 맞음 — 함수 자체
saveNote()는 그 자리에서 실행되고 결과값 undefined를 넘깁니다. 그래서 페이지가 열리는 순간 문구가 바뀌고 정작 클릭에는 반응하지 않습니다. 괄호를 빼고 이름만 넘겨야 합니다. 인수를 전달하려면 화살표 함수로 감쌉니다.
saveBtn.addEventListener("click", () => saveNote("임시저장"));
함수를 값처럼 넘기는 구조가 낯설다면 자바스크립트 함수 편의 콜백 부분을 다시 보십시오.
증상 3: 어떤 요소가 눌렸는지 알 수 없습니다
이벤트가 발생하면 브라우저는 사건 정보를 담은 객체를 콜백의 첫 인수로 넣어 줍니다. 보통 e로 받습니다.
document.querySelector("#noteList").addEventListener("click", function (e) {
console.log(e.target); // 실제로 클릭된 요소
console.log(e.currentTarget); // 이벤트를 연결한 요소
});
e.target은 사용자가 진짜로 누른 요소, e.currentTarget은 addEventListener를 붙여 둔 요소입니다. 목록 전체에 이벤트를 걸었을 때 e.target이 결정적입니다.
증상 4: 폼을 제출하면 화면이 새로 고쳐집니다
자주 쓰는 이벤트부터 봅니다.
| 이벤트 이름 | 발생 시점 |
|---|---|
click |
버튼이나 목록을 눌렀을 때 |
input |
입력창의 글자가 바뀔 때마다 |
submit |
폼이 제출될 때 |
keydown |
키를 누른 순간 |
form은 제출되면 페이지를 다시 불러옵니다. 자바스크립트로 처리하려면 그 기본 동작을 막아야 합니다.
const titleInput = document.querySelector("#titleInput");
document.querySelector("#noteForm").addEventListener("submit", function (e) {
e.preventDefault(); // 새로 고침 방지
if (titleInput.value.trim() === "") {
status.textContent = "제목을 입력해 주십시오.";
return;
}
status.textContent = `"${titleInput.value}" 글을 등록했습니다.`;
});
titleInput.addEventListener("input", (e) => {
console.log("글자 수: " + e.target.value.length);
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") status.textContent = "";
});
e.preventDefault()는 브라우저가 원래 하려던 일을 취소하며 링크 이동을 막을 때도 씁니다. e.key에는 누른 키 이름이 들어 있습니다.
증상 5: 나중에 추가한 항목은 클릭이 안 됩니다
이벤트는 발생한 요소에서 멈추지 않고 부모를 따라 위로 전달됩니다. 이것을 버블링이라고 합니다. li를 누르면 li → ul → body 순으로 올라갑니다. 이 성질을 이용해 부모에 한 번만 이벤트를 거는 방식이 이벤트 위임입니다.
const noteList = document.querySelector("#noteList");
noteList.addEventListener("click", function (e) {
if (!e.target.classList.contains("note")) return; // 항목이 아니면 무시
e.target.classList.toggle("done");
});
// 나중에 추가된 항목도 그대로 동작합니다
const newNote = document.createElement("li");
newNote.textContent = "이벤트 정리";
newNote.classList.add("note");
noteList.appendChild(newNote);
각 li에 직접 이벤트를 걸었다면 나중에 만든 항목은 반응하지 않습니다. 요소를 붙이는 과정은 자바스크립트 DOM 편에서 다뤘고, 제이쿼리 입문의 on()도 원리는 같습니다.
핵심 정리
addEventListener— 이벤트 이름과 함수를 연결하며 괄호 없이 넘깁니다.- 이벤트 객체 — 콜백의 첫 인수이며
e.target이 실제 눌린 요소입니다. - 자주 쓰는 이벤트 —
click,input,submit,keydown이 대부분입니다. preventDefault— 폼 제출이나 링크 이동 같은 기본 동작을 취소합니다.- 버블링과 위임 — 부모에 한 번만 걸어 자식 전체를 처리합니다.