개발/Javascript

javascript에서 onclick과 addEventListener 차이점(+이벤트 캡처링 vs. 버블링)

달리초이 2023. 3. 29. 12:28

 

자바스크립트 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.targetimg 객체가 되고, 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

https://ko.javascript.info/event-delegation

728x90
반응형