
버블링을 활용하여 하나의 이벤트 핸들러에서 다른 요소의 이벤트 동작을 다룰 수 있는 패턴을 이벤트 위임(event delegation)이라고 합니다.
아래와 같이 div 안에 같은 버튼이 4개가 있다고 합시다.
function foo() {
return (
<div>
<button css={css({
display: 'flex',
justifyContent: 'center',
itemsAlign: 'center',
width: '80px',
height: '80px',
backgroundColor: 'red'
})>
<div css={css({
width: '50px',
height: '50px',
backgroundColor: 'blue'
})} />
</button>
<button>...</button>
<button>...</button>
<button>...</button>
</div>
);
}
각 button을 클릭할 때마다 작동하게 하려면 handleClick 핸들러를 4개를 작성해야 할 것입니다.
하지만, 다음과 같이 맨 위의 div 요소에 handleClick 핸들러 하나만을 작성해도 그 아래에 있는 Button에 핸들러를 작성한 것과 같은 효과를 적용할 수 있습니다.
function foo() {
const handleClick = (event) => {
if (event.target instanceof HTMLButtonElement) {
// ...
}
}
return (
<div onClick={handleClick}>
<button>...</button>
<button>...</button>
<button>...</button>
<button>...</button>
</div>
);
}
이것이 가능한 이유는 button 요소에서 div요소로 이벤트 버블링이 발생했기 때문입니다.
즉, 하위 요소에서 상위 요소로 이벤트가 전파되었습니다.
이번에는 button 요소 안의 div요소를 클릭했다고 해봅시다.
앞서 본 css에서 button과 div 사이에는 상하좌우 30px 만큼의 간격이 존재하므로 충분히 div요소를 클릭할 수 있습니다.
안쪽의 div를 클릭했을 때에도 button을 클릭한 것처럼 동작하게 하고 싶다면, closest를 사용합니다.
function foo() {
const handleClick = (event) => {
if (event.target instanceof HTMLDivElement) {
const btn = event.target.closest('button');
if (event.target === event.currentTarget) {
return;
}
if (!btn) {
return;
}
// ...
}
}
return (
<div onClick={handleClick}>
<button>...</button>
<button>...</button>
<button>...</button>
<button>...</button>
</div>
);
}
코드를 설명해보자면, handleClick은 event.target이 HTMLDivElement 일때 작동하게 됩니다.
div요소는 맨 위에 핸들러가 할당된 요소와 button 컴포넌트 안에 할당되어 있는 요소 4개가 있습니다.
이를 구분하기 위해서 현재 핸들러가 할당된 맨 위의 요소 event.currentTarget과 같으면 더이상 진행하지 않게 됩니다.
data-* 적용하기위의 패턴도 충분히 좋지만 더 간단하게 적용하고 싶다면 data-*를 각 요소에 전달해 볼 수 있습니다.
function foo() {
const handleClick = (event) => {
const bar = event.target.dataset.bar;
if (!bar) {
return;
}
// ...
}
return (
<div onClick={handleClick}>
<button data-bar="bar1">...</button>
<button data-bar="bar2">...</button>
<button data-bar="bar3">...</button>
<button data-bar="bar4">...</button>
</div>
);
}
event.stopPropagation()을 쓸 수 없습니다.