자바스크립트 onclick과 addEventListener를 둘 다 사용하다가 문득 두 메서드의 차이가 궁금해져서 간략히 정리해 본다.
onclick
- 단일 콜백 함수(리스너)만 지정 사용 가능
- 하나의 이벤트만 사용하므로 중복될 경우 앞선 이벤트를 덮어씀
- 거의 모든 브라우저에서 작동
- 구형 IE(9 이하)에서도 동작
addEventListener
- 모던 자바스크립트로 넘어오면서 새롭게 추가
- 이벤트 중첩이 가능함
- 여러 개의 이벤트를 등록해도 모두 동작(덮어쓰지 않음)
- IE9 이상 거의 모든 브라우저에서 작동
- 이벤트 전파 단계(캡처링 vs. 버블링) 조절 가능 (3번째 options 매개변수)
이벤트 캡처링 vs. 버블링 (Propagation)
기본값은 버블링 = {capture: false}
event.stopPropagation() 메서드로 전파를 중단시킬 수 있다.
<div class="one">
<div class="two">
<div class="three">
</div>
</div>
</div>
- 이벤트 전파단계
이벤트 위임(Event Delegation)
그렇다면, 버블링과 캡처링을 왜 알아야 할까?
기본적으로 이벤트가 어떻게 발생하고 전파되는지 이해를 했다면 실무적인 관점에서 왜 중요한지 알아보자.
이벤트 전파를 이해하고 나면 이벤트 핸들링 패턴인 이벤트 위임(Event Delegation)을 구현할 수 있다.
이벤트 위임은 동일한 방식으로 여러 요소에 이벤트를 주고자 할 때 유용한데, 방식은 요소마다 이벤트를 주는 것이 아니라 요소들의 공통 부모에게 이벤트를 위임하는 것이다. 이렇게 하면 부모에 할당된 핸들러에서 event.target을 이용해 선택된 하위 자식에게 이벤트를 일으킬 수 있게 된다.
event.target 과 event.currentTarget
여기서 잠깐, event.target과 event.currentTarget 에 대해 이해하는 게 중요한데, target은 이벤트가 일어나는 요소를 말하고 currentTarget은 이벤트가 위임된 요소를 일컫는다. 말보다 코드가 빠른 여러분을 위해 아래 예시를 보자.
<div class="parents">
<img src="image.jpg" class="child">
</div>
--- 위와 같은 구조가 있을 때,
const 부모 = document.querySelector(".parents");
부모.addEventListener("click", (event) => {
console.log(event.target); // img 객체
console.log(event.currentTarget); // div 객체
});
위 구조에서 event.target은 img 객체가 되고, event.currentTarget은 이벤트를 위임받은 부모 요소, 즉 div 객체가 된다. 그럼 다시 돌아가서, 만약 이벤트를 걸어야 할 대상이 동적으로 추가되거나 삭제될 수 있는 상황이라면 해당 요소에 이벤트를 어떻게 걸 수 있을까?
위 내용을 제대로 이해했다면 쉽게 답이 떠오른다. 결국 동적 요소를 감싼 부모 요소에 이벤트를 위임하고 event.target으로 실제 이벤트가 일어난 요소를 받아 버블링으로 이벤트를 일으키는 패턴이면 해결되는 것이다. (좀 더 자세하고 유용한 정보를 알고 싶은 분들은 여기를 참조하세요. 이벤트 위임 활용과 행동패턴은 정말 꿀팁이네요.)
메모리 문제
mdn 문서를 보다 사소하지만 기본적인 내용이라 가져왔다.
const els = document.getElementsByTagName('*');
// 상황 1
for(let i=0 ; i < els.length; i++){
els[i].addEventListener("click", function(e){/*do something*/}, false);
}
// 상황 2
function processEvent(e){
/* do something */
}
for(let i=0 ; i < els.length; i++){
els[i].addEventListener("click", processEvent, false);
}
위와 같은 상황에서 상황 1은 반복할 때마다 새로운 익명 함수가 생성되지만, 상황 2는 반복문 외부에 정적 함수를 사용하기 때문에 더 적은 메모리 공간만 사용하게 된다. 그리고 상황 1은 익명 함수에 대한 참조를 유지하지 않기 때문에 removeEventListener()를 호출해서 이벤트를 없앨 수 없다.
[참고]
https://developer.mozilla.org/ko/docs/Web/API/EventTarget/addEventListener
https://developer.mozilla.org/ko/docs/Web/API/Event/target
https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
'개발 > Javascript' 카테고리의 다른 글
Javascript Event Loop로 알아보는 Event Driven Programming (0) | 2025.01.12 |
---|