안녕하세요 웹파트 배영경입니다🤗
이번에는 자바스크립트의 이벤트위임에 대해 알아보았습니다!
2주차 과제를 구현할 때 각 체크박스마다 이벤트 리스너를 개별적으로 등록하는 방식을 사용하였는데, 많은 DOM 요소에 이벤트 리스너를 등록하면 성능상 좋지 않다는 것을 알게되었습니다. 이벤트 위임을 하면 상위 요소에서 이벤트를 처리하고, 이벤트가 발생한 요소에 맞게 로직을 처리할 수 있습니다.
이벤트 위임을 이해하기 위해서는 이벤트 버블링과 캡처링에 대한 이해가 필요합니다.
이벤트 캡처링(Event Capturing)
이벤트 캡처링은 이벤트가 최상위 요소에서부터 자식 요소로 전달되는 과정. 이벤트가 위에서부터 내려오는 방식으로 전파된다.
캡처링과 버블링 모두 기본적으로 발생하지만, 브라우저는 대부분 이벤트 버블링 방식으로 이벤트를 처리한다. 그러나 addEventListener의 세 번째 인자를 true로 설정하면, 이벤트가 캡처링 단계에서 처리되도록 할 수 있다.
element.addEventListener('click', handler, true); // 캡처링 단계에서 처리
- 예시
<div id="parent"> <button id="child">Click</button> </div>
이 코드에서는 부모 요소에 이벤트 리스너를 캡처링 단계에서 등록한다. 따라서, 자식 요소인 버튼을 클릭했을 때 먼저 부모의 이벤트 리스너가 실행된다.document.getElementById('parent').addEventListener( 'click', () => { console.log('이벤트 캡처링 부모요소'); }, true // true로 설정하면 캡처링 단계에서 실행 );
이벤트 버블링(Event Bubbling)
이벤트 버블링은 이벤트가 발생한 요소에서 시작하여 상위 요소로 전파되는 것.
예를 들어, <button> 요소를 클릭하면 이 클릭 이벤트가 해당 버튼에서 먼저 발생한 후, 부모 요소를 거쳐 최상위 요소인 <body>까지 전파된다.
- 예시
위 코드에서 버튼을 클릭하면, 클릭 이벤트가 발생한 요소에서 먼저 “자식요소”가 출력되고, 이후 “부모요소”가 출력된다.document.getElementById('parent').addEventListener('click', () => { console.log('부모요소'); }); document.getElementById('child').addEventListener('click', () => { console.log('자식요소'); });
이벤트 위임(Event Delegation)
이벤트 위임(Event Delegation)은 JavaScript에서 효율적으로 이벤트 리스너를 관리하는 기법.
특히 동적으로 생성되는 요소나 반복되는 여러 요소에 대해 개별적으로 이벤트 리스너를 붙이는 대신, 상위 요소에 리스너를 등록하여 자식 요소에서 발생하는 이벤트를 처리하는 방식이다.
이를 통해 많은 자식 요소에 대해 각각 리스너를 등록하지 않아도 되어 성능 향상과 코드 간소화를 할 수 있다.
즉, 부모 요소에 하나의 이벤트 리스너를 등록해, 그 하위 자식 요소들에서 발생하는 이벤트를 처리하는 방법이다.
- 예시
<ul id="itemList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul>
여기서 ul 요소에 클릭 이벤트 리스너를 등록했다.// 부모요소(ul)에 이벤트 리스너 등록 document.getElementById('itemList').addEventListener('click', (event) => { if (event.target.tagName === 'LI') { console.log(`${event.target.textContent} 이 클릭되었습니다`); } });
사용자가 li 요소를 클릭하면, 이벤트가 ul 로 버블링되고 event.target을 통해 실제 클릭한 요소가 li 인지 확인한 후 로직을 처리한다.
이를 통해 li 마다 이벤트 리스너를 등록할 필요 없이, 하나의 리스너로 여러 요소의 이벤트를 처리할 수 있다.
이벤트 버블링과 캡처링이 발생하는 순서
🤔 저는 처음에 이벤트 버블링과 캡처링은 완전히 반대되는 과정인데, 어떤 경우에 버블링되고 어떤 경우에 캡처링이 되는지 이해가 잘 되지 않았습니다.
이벤트가 발생하면 다음과 같은 세 가지 단계를 거쳐 이벤트가 전파된다.
- 캡처링 단계: 이벤트가 최상위 요소에서부터 이벤트가 발생한 요소까지 내려간다.
- 타깃 단계: 이벤트가 발생한 실제 요소에서 실행된다.
- 버블링 단계: 이벤트가 발생한 요소에서 최상위 요소로 거슬러 올라가며 전파된다.
캡처링과 버블링은 둘 중 하나만 발생하는 것이 아니라, 항상 순서대로 발생한다. 다만, 기본적으로는 버블링 단계에서만 이벤트 리스너가 반응하기 때문에 캡처링은 눈에 띄지 않는 것.
즉, JavaScript는 기본적으로 캡처링을 비활성화하고 버블링만 활성화한다. 그래서 대부분의 경우 이벤트 리스너를 등록하면 버블링 단계에서 이벤트가 발생한다.
- 예시 : 하나의 클릭 이벤트가 캡처링 단계와 버블링 단계 모두에서 처리되는 경우
<div id="parent"> <button id="child">Click</button> </div>
실행 결과:// 캡처링 단계에서 실행 (캡처링 활성화) document.getElementById('parent').addEventListener('click', () => { console.log('캡처링 단계에서 부모요소 div'); }, true); // 버블링 단계에서 실행 (기본 동작) document.getElementById('parent').addEventListener('click', () => { console.log('버블링 단계에서 부모요소 div'); }); // 버튼에 클릭 리스너 추가 document.getElementById('child').addEventListener('click', () => { console.log('자식요소 버튼 클릭'); });
- 캡처링 단계에서 부모요소 div (캡처링 단계에서 부모 요소)
- 자식요소 버튼 클릭 (버튼 요소에서 이벤트 발생)
- 버블링 단계에서 부모요소 div (버블링 단계에서 부모 요소)
효율적인 이벤트 처리 방식
이벤트 위임을 이용하면 여러 자식 요소에 이벤트 리스너를 각각 붙이는 것보다 효율적이다. 특히 동적으로 추가되거나 제거되는 요소의 경우, 모든 변화에 대응하여 리스너를 추가하거나 제거하는 것이 복잡해진다. 대신 부모 요소에 이벤트 리스너 하나만 등록하면 자식 요소의 변화에 상관없이 이벤트 처리가 가능하다.
- 예시 : 각 체크박스에 개별적으로 이벤트 리스너 추가
// 전체 체크박스에서 하나라도 해제되면 맨위 체크박스도 같이 해제
const checkboxes = document.querySelectorAll('.checkBox');
for(const checkbox of checkboxes) {
checkbox.addEventListener("click", () => {
const totalCnt = checkboxes.length;
const checkedCnt = document.querySelectorAll('.checkBox:checked').length;
if(totalCnt === checkedCnt){
checkAll.checked = true;
}
else{
checkAll.checked = false;
}
});
}
위 코드에서 for 반복문을 통해 .checkBox 클래스가 있는 각 체크박스에 click 이벤트 리스너를 개별적으로 추가하고 있다.
이 코드 구조에서는 checkboxes의 개수가 많아질수록 addEventListener가 여러 번 호출되며 성능에 영향을 줄 수 있다.
이러한 상황에서 이벤트 위임을 사용하여 부모 요소(테이블 또는 상위 컨테이너)에 이벤트 리스너를 한 번만 추가하고, 이벤트가 발생한 요소가 체크박스인지 확인하는 방식으로 개선할 수 있다.
document.querySelector('.table').addEventListener('click', (event) => {
if (event.target.classList.contains('checkBox')) {
// 클릭한 요소가 체크박스인 경우 처리
// ~~~ 로직
}
});
이렇게 하면 <table> 요소에 이벤트 리스너를 하나만 등록하고, 체크박스를 클릭할 때마다 해당 클릭을 처리할 수 있다.
주의사항
- 정확한 이벤트 타겟팅: 이벤트가 상위 요소로 전달되므로, 하위 요소에서만 이벤트를 처리해야 한다.
이를 위해 event.target이나 event.currentTarget을 적절히 활용하여 이벤트가 발생한 요소를 정확하게 확인해야 한다. - 버블링이 없는 이벤트: focus, blur 같은 일부 이벤트는 버블링되지 않으므로, 이벤트 위임을 사용할 수 없다.
'2주차' 카테고리의 다른 글
호이스팅(hoisting) 에 관하여 (0) | 2024.10.28 |
---|---|
2주차: JSON 알아보기 (0) | 2024.10.28 |
메모이제이션 - useCallback, useMemo + React.memo (0) | 2024.10.25 |
로컬스토리지, 세션스토리지, 쿠키 (0) | 2024.10.25 |
나랑 약속해줘.. Promise..🤫 (0) | 2024.10.24 |