JS 이벤트 위임 적용

불꽃남자·2020년 9월 9일
0

오늘은 이벤트 위임을 현재 진행중인 grand busan 클론코딩 프로젝트에 적용시켜보려고 한다.

현재 내 프로젝트의 Header 컴포넌트의 메뉴 리스트 구성은 이렇게 되어있다.

//Header.js
//...

<HeaderList>
  {listArray.map((listItem, index) => {
    return (
      <HeaderListItem key={index} isOn={isOn} id={index}>
        <a
          href="/"
          onMouseEnter={(e) => {
            setIsHover(true);
            setIsOn(index);
          }}
        >
          {listItem.listItem}
        </a>
        {subMenuFn(listItem)}
      </HeaderListItem>
    );
  })}
</HeaderList>

//...

HeaderList라는 ul태그 안에 HeaderListItem이라는 li태그를 map으로 생성하고, li태그 하나하나에 onMouseEnter 이벤트 핸들러를 적용시키고 있는 모습이다.
나는 이 코드에 큰 의문을 느끼지 못 했다... 이벤트 위임을 알기 전까진 말이다!

이벤트 위임

이벤트 위임이란 무엇인가?
이벤트 캡쳐링과 이벤트 버블링을 이용해서, 하나의 조상요소에서 그 하위요소들의 이벤트를 모두 컨트롤하는 것을 이벤트 위임이라고 할 수 있다. 즉, 하위요소가 조상요소에게 이벤트를 위임하는 것이다.

그럼 내 코드를 리팩토링하며 더 자세히 알아보자.

이벤트 위임 적용

//Header.js
//...

<ul>
  {listArray.map((listItem, index) => {
    return (
      <li key={index} isOn={isOn} id={index}>
        <a
          href="/"
          onMouseEnter={(e) => {
            setIsHover(true);
            setIsOn(index);
          }}
        >
          {listItem.listItem}
        </a>
        {subMenuFn(listItem)}
      </ul>
    );
  })}
</li>

//...

이해를 돕기 위해 컴포넌트 명들을 HTML 태그명으로 변경했다.

이벤트 위임을 알고 난 후 이 코드를 다시보니 '이런 더러운 코드가 다 있나!' 싶다.
a 태그의 갯수만큼 onMouseEnter의 이벤트 리스너의 갯수도 늘어나고, 그만큼 리소스를 잡아먹고 있는 것이다. 그럼 이제 이벤트 위임을 적용해보겠다.

//Header.js
//...

<ul
  onMouseEnter={(e) => {
    setIsHover(true);
    console.log(e.target);
  }}
>
  {listArray.map((listItem, index) => {
    return (
      <li key={index} isOn={isOn} id={index}>
        <a href="/">{listItem.listItem}</a>
        {subMenuFn(listItem)}
      </li>
    );
  })}
</ul>

//...

우선 a 태그에 붙어있던 onMouseEnter를 제거했고 반복되는 li들의 공통 조상요소인 ul에 onMouseEnter 이벤트 리스너를 달아주었다. 이제 a태그에 mouseEnter 이벤트가 일어나면 어떻게 될까?

보다시피 a 태그에는 이벤트리스너가 없음에도 불구하고 ul 요소에서 이벤트를 감지하여 console.log(e.target) 을 실행해주고 있다.

그리고 발생하는 이슈

이벤트를 위임받은 요소에서 일어난 이벤트도 감지된다


당연한 이야기다. ul 태그에 이벤트리스너를 등록했기 때문에, ul 태그 자체에서 일어나는 이벤트도 감지된다. 위의 사진은 ul 태그의 padding에 onMouseEnter 되었을 때의 모습이다.
이 문제는 user agent stylesheet의 ul태그의 padding값을 0으로 초기화하고 margin-left를 더 줘서 해결했다.

onMouseEnter가 작동하지 않는다?


a 태그에서 다른 a 태그로 마우스를 옮길 때 onMouseEnter이벤트가 감지되질 않는다. a 태그에 onMouseEnter 이벤트가 일어날 때 마다 ul 태그의 이벤트 리스너가 감지해야 하는 것이 아닌가?

라고 생각했었다. onMouse 이벤트에 관한 글을 읽기 전 까지는!
onMouseEnter 이벤트는 요소 밖으로 포인터가 나갔다 오지 않으면 계속 발생하지 않는다. 그리고 onMouseOver 이벤트는 요소 영역을 벗어나지 않아도 자식 요소에 들어갈 때에 발생한다.
즉 위의 경우에는 onMouseOver 이벤트 리스너를 달아주어야 한다는 것이다.

onMouseOver로 바꿔주자 감지가 잘 되는 모습

적용 완료

남은 일은 mouseOver된 a 요소의 부모인 li 요소의 index값을 읽어와서 setIsOn(index) 해주는 것 뿐이다.

<ul
  onMouseOver={(e) => {
    setIsHover(true);
    setIsOn(parseInt(e.target.parentElement.id));
    // isOn과 같은 id를 가지고 있는 li의 SubMenu 컴포넌트가 보여진다.
  }}
>
  {listArray.map((listItem, index) => {
    return (
      <li key={index} isOn={isOn} id={index}>
        <a href="/">{listItem.listItem}</a>
        {subMenuFn(listItem)}
      </li>
    );
  })}
</ul>


적용이 완료되었다!

마치며

요 근래 프론트엔드 기술 면접에 관련된 글들을 찾아보면서 그동안 내가 얼마나 프론트엔드에 대해 무지했는지 깨우치고 있다. 이벤트 위임이란 개념도 글을 읽기 전까지는 알지 못 했었다.
프론트엔드 개발자가 갖춰야 마땅한 지식임에도 까맣게 모르고 있었다는 사실이 부끄러웠고, 한 편으로는 이제 알게 되어서 굉장히 기쁘다.

또 이렇게 블로그에 공부한 내용을 게시하며 배워나가니 더 정확하고 꼼꼼하게 배우게 된다. 좋은 흐름이다. 이 흐름에 맞추어 앞으로 걸어나가겠다.

profile
프론트엔드 꿈나무, 탐구자.

0개의 댓글