이벤트 위임과 이벤트 버블링 정리
1. 이벤트 위임 (Event Delegation)
개념
- 이벤트 위임은 부모 요소에 단일 이벤트 리스너를 추가하여 자식 요소의 이벤트를 처리하는 방식.
- 이벤트 버블링을 활용하여 자식 요소에서 발생한 이벤트가 부모 요소로 전파되도록 함.
장점
- 성능 최적화:
- 많은 자식 요소에 각각 이벤트 리스너를 추가할 필요 없이 부모에 단일 리스너만 추가하면 됨.
- 동적 요소 지원:
- DOM에 새로 추가된 자식 요소도 부모의 이벤트 리스너로 처리할 수 있음.
단점
- 추가 조건 필요:
- 이벤트가 버블링되므로, 특정 자식 요소만 처리하려면 조건문(
if (event.target.tagName === 'I')
)을 추가해야 함.
- 복잡성 증가:
- 단순한 구조에서는 개별 리스너를 사용하는 것이 더 직관적일 수 있음.
2. 이벤트 버블링 (Event Bubbling)
개념
- DOM 이벤트 모델에서, 이벤트가 발생하면 가장 안쪽의 요소(타겟)에서 시작하여 부모 요소로 전파됨.
- 예를 들어, 자식 요소에서
click
이벤트가 발생하면 해당 이벤트는 부모, 조부모 등 상위 요소로 전달됨.
이벤트 캡처와의 차이
- 캡처(Capturing): 이벤트가 최상위 요소에서 시작하여 타겟 요소로 전파됨.
- 버블링(Bubbling): 타겟 요소에서 시작하여 최상위 요소로 전파됨.
3. mouseenter
vs mouseover
특성 | mouseenter | mouseover |
---|
버블링 | 버블링되지 않음 | 버블링됨 |
트리거 조건 | 자신만 트리거 | 자신과 자식 모두 트리거 |
사용 사례 | 자식 요소 무시 | 자식 포함 처리 |
mouseenter
: 마우스가 해당 요소에 진입할 때만 트리거되며, 자식 요소에는 영향을 받지 않음.
mouseover
: 마우스가 해당 요소 또는 그 자식 요소에 진입할 때마다 트리거됨.
장단점 비교
mouseenter
:
- 버블링되지 않으므로 불필요한 이벤트 트리거를 방지할 수 있음.
- 자식 요소가 없는 구조에서 효율적임.
mouseover
:
- 버블링되므로 부모 컨테이너에서 자식 요소의 이벤트를 감지할 수 있음.
- 이벤트 위임 방식에 적합함.
4. mouseleave
vs mouseout
특성 | mouseleave | mouseout |
---|
버블링 | 버블링되지 않음 | 버블링됨 |
트리거 조건 | 자신만 트리거 | 자신과 자식 모두 트리거 |
사용 사례 | 자식 요소 무시 | 자식 포함 처리 |
mouseleave
: 마우스가 해당 요소 전체에서 벗어날 때만 트리거되며, 자식 요소에는 영향을 받지 않음.
mouseout
: 마우스가 해당 요소 또는 그 자식 요소에서 벗어날 때마다 트리거됨.
장단점 비교
mouseleave
:
- 버블링되지 않으므로 불필요한 이벤트 트리거를 방지할 수 있음.
- 자식 요소가 없는 구조에서 효율적.
mouseout
:
- 버블링되므로 부모 컨테이너에서 자식 요소의 이벤트를 감지할 수 있음.
- 이벤트 위임 방식에 적합함.
코드 비교 및 분석
원래 코드 (개별 리스너 + mouseenter
, mouseleave
)
stars.forEach((star) => {
star.addEventListener('mouseenter', handleMouseEnter);
star.addEventListener('mouseleave', handleMouseLeave);
star.addEventListener('click', handleClick);
});
- 각 별(
i
) 태그에 개별적으로 리스너를 추가.
- 이 방식은 구조가 단순하고 코드 가독성이 높음.
- 별점 개수가 많아질 경우 성능 저하 가능성이 있음.
중간 코드 (이벤트 위임 + mouseover
, mouseout
)
container.addEventListener('mouseover', (event) => {
if (event.target.tagName === 'I') {
const ratingValue = parseInt(event.target.dataset.ratingValue, 10);
stars.forEach((star, index) => {
star.classList.toggle('hovered', index {
stars.forEach((star) => star.classList.remove('hovered'));
});
container.addEventListener('click', (event) => {
if (event.target.tagName === 'I') {
const ratingValue = parseInt(event.target.dataset.ratingValue, 10);
stars.forEach((star, index) => {
star.classList.toggle('selected', index < ratingValue);
});
const ratingChangeEvent = new CustomEvent('rating-change', {
detail: ratingValue,
});
container.dispatchEvent(ratingChangeEvent);
}
});
- 부모 컨테이너(
container
)에 단일 리스너를 추가하여 모든 별의 이벤트를 처리함.
- 동적으로 추가된 별점도 자동으로 처리 가능.
- 성능 최적화 측면에서 유리함