바닐라js로 컴포넌트를 만들었을 때 이벤트 위임을 추상화한 메서드에서 많은 문제점을 발견했다.
addEventToTarget(
eventType: string,
selector: string,
callback: (event?: Event) => void,
) {
const children = [...Array.from(this.$target.querySelectorAll(selector))];
const isTarget = (target: EventTarget | null): boolean | Element | null => {
if (target instanceof HTMLElement) {
return children.includes(target) || target.closest(selector);
}
return null;
};
this.$target.addEventListener(eventType, event => {
if (!isTarget(event.target)) return false;
callback(event);
});
}
selector로 item을 지정한 경우
// 원래 의도
<ul class="list">
<li class="item">
<ul class="list">
<li class="item">
<i></i> // 이 부분이 타겟이 됐을 때에도 동작되게 하는게 의도
</li>
<li class="item">
<i></i>
</li>
</ul>
</li>
</ul>
// 예외사항
<ul class="list">
<li class="item">
<ul class="list"> // closest으로 인해 이 요소가 target이 되어도 핸들러가 동작되게 됨
<li class="item">
<i></i>
</li>
<li class="item">
<i></i>
</li>
</ul>
</li>
</ul>
조건
1. 이벤트 타겟이 select인지 확인해야함
2. 이벤트 타겟이 select의 자식인지 확인해야함
3. 여러 개의 셀렉터와 그에 매칭되는 로직이 한 핸들러 함수 내에서 실행되어야 함
인터페이스
addEventToTarget2('click', ['selector1','selector2','selector3'], [()=>'', ()=>'', ()=>''])
시도
addEventToTarget(
eventType: string,
selectorArr: string[],
callbackArr: ((event?: Event) => void)[],
) {
this.$target.addEventListener(eventType, (event: any) => {
const count = selectorArr.length;
for (let i = 0; i < count; i++) {
const children = Array.from(this.$target.querySelectorAll(selectorArr[i]));
const isTarget = (
target: EventTarget | null,
): boolean | Element | null => {
if (target instanceof HTMLElement) {
return children.includes(target) || target.closest(selectorArr[i]);
}
return null;
};
if (!isTarget(event.target)) return false;
callbackArr[i](event);
}
});
}
너무 복잡하고 핸들러가 실행될 때마다 돔에 접근해야하는게 별로 좋은 방법은 아닌 것 같다..
더 좋은 방법을 찾아봐야겠지만 그냥 추상화하지 말고 필요할 때마다 직접 작성하는게 더 나을 것 같다.
참고한 코드라도 의심을 가지고 세세하게 살펴보고 나서 사용해야한다는 것을 느꼈다.