[TIL] 자바스크립트 이벤트, 버블링과 캡처링

이진호·2023년 11월 3일
0

TIL

목록 보기
20/66

이벤트 처리 과정


[출처 : javascript info]

  1. 이벤트가 발생하면 이벤트가 발생한 가장 안쪽 요소가 타깃 요소(event.target)가 된다.
  2. DOM 트리를 따라 내려가면서 addEventListener(...,true)로 할당된 핸들러를 동작시킨다. // 캡처링
  3. 이후 타깃 요소에 설정된 핸들러가 호출된다.
  4. 이후에 이벤트가 target부터 시작해서 다시 최상위 노드까지 전달되면서 각 요소에 on<event>로 할당된 핸들러와 addEventListener로 할당한 핸들러를 동작시키는데 이때 세번째 인수가 없거나, false, {capture: false}인 핸들러만 호출한다. // 버블링

target, currentTarget

이벤트 객체의 속성 중 target과 currentTarget이 있는데 target은 이벤트가 발생한 가장 안쪽 요소 를 의미한다.
currentTarget은 이벤트 핸들링을 하는 현재 요소( 핸들러가 실제로 할당된 요소)를 가르킨다.

event.target은 실제 이벤트가 시작된 '타깃'요소로 버블링이 진행되어도 변하지 않는다.

버블링 중단

버블링은 중단을 시킬 수 있는데 event.stopPropagation() 메서드를 통해서 더 이상의 버블링을 중단 시킬 수 있다.

이때, stopPropagation은 하나의 요소에 여러 개의 이벤트 핸들러가 바인딩 된 경우에 핸들러 중 하나가 멈추더라도 나머지 핸들러는 여전히 동작한다.
요소에 할당된 다른 핸들러의 동작도 막으려면 event.stopImmediatePropagation()을 사용해야 한다.

이벤트 위임

이벤트 위임은 이벤트 핸들링 패턴 중 하나이다.
버블링과 캡처링을 활용하여 하나의 이벤트를 여러 요소들의 공통으로 사용할 수 있다면 상위 요소 하나에게만 이벤트를 적용시켜서 이벤트 핸들러를 각 요소에 바인딩 하지 않아도 된다는 장점이 있다.

이벤트 위임은 다음과 같은 알고리즘으로 동작한다.
1. 컨테이너에 하나의 핸들러를 할당한다.
2. 핸드러의 event.target 을 사용해 이벤트가 발생한 요소가 어디인지 알아낸다.
3. 원하는 요소에서 이벤트가 발생됐다고 확인되면 이벤트를 핸들링 한다.

이벤트 위임 활용

만약 3개의 버튼이 있고, 각각의 기능을 수행할 수 있는 메서드가 주어졌을 때 한번의 이벤트 할당으로 3개의 버튼에 이벤트 위임을 할 수 있다.

<div id="menu">
  <button data-action="save">저장하기</button>
  <button data-action="load">불러오기</button>
  <button data-action="search">검색하기</button>
</div>
<script>
  class Menu {
  	constructor(elem) {
  		this._elem = elem;
  		elem.onclick = this.onClick.bind(this);
  	}
  	save() {alert('저장하기')}
  	load() {alert('불러오기')}
  	search() {alert('검색하기')}
  	onClick(event) {
  		let action = event.target.dataset.action;
  		if(action) {
  			this[action]();
  		}
  	}
  }
</script>

프로젝트 적용

무비 탑 리스트 팀 과제

첫번째 적용

바닐라 자바스크립트로 SPA를 구현하기 위해서 routing이라는 함수를 구현하고, a 태그에 data-link 속성을 바인딩해서 주소를 적어놨다.
그리고 document에 하나의 이벤트를 추가하여 모든 a 태그에 이벤트 핸들러를 등록하지 않고 a 태그에 저장된 페이지로 이동할 수 있도록 함수를 작성하였다.

document.addEventListener('click',(e) => {
  // a 태그에 data-link속성을 걸어서
  // 다음 라우팅 될 주소를 저장해놨다.
  	if(e.target.matches('[data-link]') {
       e.preventDefault();
  	   history.pushState(e.target.href,null,e.target.href);
  		routing(router); // vanillar javascript로 SPA 구현
    }
})

두번째 적용

팀원이 검색 창에서 검색을 할 시에 어느 곳이든 '/'를 눌르면 검색창이 포커즈가 될 수 있도록 구현을 하였는데 코멘트를 남기던 도중에도 '/'를 눌르면 검색 창으로 올라가는 이슈가 있었다. 해당 이슈를 해결하기 위해서 아래와 같은 방식으로 적용했다.

window.addEventListener('keydown', e => {
  // select은 유틸 함수로
  // select(selector) === document.querySelector(selector) 역할을 한다.
  	const slash = select(`input[data-key=${e.code}`);
  	if(!slash) return;
  
  // selectAll은 select과 같은데 querySelectorAll이다.
  	const notActive = Array.from(selectAll('input')).every(el => el !== document.activeElement);
  
	if(notActive) slash.focus();
  	...
})

벨로그 크롬 익스텐션

벨로그 크롬 익스텐션 개발 중에 툴바에 들어가있는 템플릿 버튼을 눌르면 드롭다운 메뉴처럼 템플릿을 선택할 수 있도록 개발하였다.
그 과정에서 템플릿 버튼이거나 혹은 그 안에 모든 요소들을 선택했을 경우에는 드롭다운 메뉴가 닫히지 않고 그 외의 요소를 클릭했을 경우에는 드롭다운 메뉴가 닫힐 수 있도록 구현하엿다.

document.addEventListener('click',(e) => {
  const selectors = 
        ['.template-wrapper *',
         '.template-btn *', 
         '.template-btn',
         '.template-wrapper'
        ];
  const matched = selectors.some(selector => e.target.matches(selector);
                                 
  if(matched) {
	return;
  }
  // 현재 드롭다운 메뉴가 열려있으면 닫는다.
  const $templateContentContainer = select()('.template-content-container');
  if (
    $templateContentContainer &&
    $templateContentContainer.classList.contains('active')
  ) {
    $templateContentContainer.classList.remove('active');
  }
})
profile
dygmm4288

0개의 댓글