[TIL] 0614

yoon Y·2022년 6월 15일
0

2022 - TIL

목록 보기
96/109

바닐라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);
    });
  }

문제점

  • template이 재실행되면 기존에 있던 DOM이 날라가고 다시 그려지는 과정에서 children배열의 노드들이 무용지물 → 첫 마운트 이후 한번만 실행되기 때문에
  • […]와 Array.from이 중복
  • 한 셀렉터에 한 핸들러가 매칭되어 의도한 대로의 이벤트 위임을 할 수 없다.
  • target의 부모에 select와 같은 클래스명을 쓸 경우 closest가 의도한대로 동작하지 않을 수 있다.
    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);
      }
    });
  }

너무 복잡하고 핸들러가 실행될 때마다 돔에 접근해야하는게 별로 좋은 방법은 아닌 것 같다..
더 좋은 방법을 찾아봐야겠지만 그냥 추상화하지 말고 필요할 때마다 직접 작성하는게 더 나을 것 같다.
참고한 코드라도 의심을 가지고 세세하게 살펴보고 나서 사용해야한다는 것을 느꼈다.

profile
#프론트엔드

0개의 댓글