ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [개발일지03/🌉포폴사이트 JS] 이벤트 위임으로 addEventListener 사용 빈도 줄이기
    프로젝트 기록/포트폴리오 사이트 2024. 3. 20. 21:27
    햄버거 메뉴 토글 버튼 구현 기초 👉 https://dev-thinking.tistory.com/21

     

    🙌 햄버거 메뉴 버튼 구현 시 메뉴 팝업이 열리면 햄버거 메뉴 아이콘이 X로 변경되어야 했다. 이때 svg 이미지를 사용했기에 우선은 각각의 버튼에 addEventListener를 달아줬는데, 중복 코드가 많아져서 좀 더 개선할 방법이 없나 고민하다가 이벤트 위임으로 해결할 수 있다는 것을 알게 되었다.

     

    🧐 기존 코드 살펴보기

    * 지금부터는 설명의 편의를 위해 addEventListener를 이벤트 리스너로 명명한다.

    기존 코드는 아래와 같이 햄버거 메뉴 버튼을 클릭했을 때와 닫기(X) 버튼을 클릭했을 때 각각 이벤트 리스너를 실행하고 있었다. 하지만 로직이 다른 것도 아니고 이미지만 변경되는 상황에서 이벤트 리스너를 두 번 호출하는 것은 그다지 효율적이지 못한 느낌이라 중복 코드를 제거해 봐야겠다는 생각이 들었다.

    // 햄버거 메뉴 버튼 클릭시
    $menuBtn.addEventListener("click", () => {
      // 버튼 토글 구현 추가하기
      $mobileMenu.classList.toggle("hidden");
    
      // 버튼이 눌리면 X 버튼으로 아이콘 변경
      if (!$menuBtn.classList.contains("hidden")) {
        $menuBtn.classList.toggle("hidden");
        $menuBtnClose.classList.toggle("hidden");
      }
    });
    
    // 닫기 버튼 클릭시
    $menuBtnClose.addEventListener("click", () => {
      $mobileMenu.classList.toggle("hidden");
    
      if (!$menuBtnClose.classList.contains("hidden")) {
        $menuBtnClose.classList.toggle("hidden");
        $menuBtn.classList.toggle("hidden");
      }
    });

     

    💡 이벤트 위임이란?

    비슷한 방식으로 여러 요소를 다뤄야 할 때 요소마다 핸들러를 할당하지 않고, 요소의 공통 조상에 이벤트 핸들러를 단 하나만 할당하여 자식 요소를 핸들링하는 것 - 모던 JavaScript 튜토리얼

     

    🛠️ 코드 개선 하기

    현재 html 마크업 구조는 아래와 같다.

    <!-- 버튼 -->
    <div
      id="btn-wrapper"
      class="inset-y-0 left-0 flex items-center md:hidden"
    >
      <button
        id="btn-open"
        type="button"
        data-toggle="open"
        class="text-black inline-flex items-center justify-center menu-btn"
      >
        <svg>
            <path />
        </svg>
     </button>
     <button
        id="btn-close"
        type="button"
        data-toggle="close"
        class="hidden text-black items-center justify-center menu-btn"
      >
        <svg>
            <path />
        </svg>
     </button>
    </div>
    <!-- 메뉴 리스트 -->

     

    클릭되는 요소들을 알아 와야 하기에 총 4개의 id를 각각의 요소에 할당해 줬다.

    태그명 div
    (button의 부모 요소)
    button
    (햄버거 메뉴 버튼)
    button
    (X 버튼)
    nav list
    id명 btn-wrapper btn-open btn-close mobile-menu

     

    이제 부모 요소인 div에 이벤트 리스너를 달아주자. 함수가 이벤트 리스너로 호출이 되면 첫 번째 매개변수 자리에 자동으로 event 객체가 할당된다. 이 event 객체의 속성을 사용하여 선택한 요소를 알아낼 수 있다.

    const menuBtn = document.querySelector("#btn-wrapper");
    const menuBtnOpen = document.querySelector("#btn-open");
    const menuBtnClose = document.querySelector("#btn-close");
    const mobileMenu = document.querySelector("#mobile-menu");
    
    
    menuBtn.addEventListener("click", (e) => {
    });

     

    event 객체의 target 속성을 통해 클릭한 요소의 정보를 알아낼 수 있다. 이때 우리가 필요한 것은 클릭한 메뉴 '버튼'인데 현재 button 요소 안에 svg -> path 요소에 대한 정보를 받아오는 것을 확인할 수 있다.

    menuBtn.addEventListener("click", (e) => {
      let elem = e.target;
      console.log("elem: ", elem);
    });

    클릭한 요소에 대한 정보

     

    👀 이미지 정보를 받아오는 이유

    button 태그가 svg 태그를 감싸고 있기 때문에 button 태그가 선택되겠지?라는 생각을 하고 있을지도 모른다. 나도 그랬다. event.target 속성은 이벤트 리스너를 걸어둔 요소의 기준이 아니라 클릭 이벤트가 발생한 위치 기준임을 기억하자. 즉, button 안에 있는 햄버거 메뉴와 X 버튼이 svg라는 또 하나의 요소로 들어가 있기 때문에 우리는 '이미지'를 클릭한 것이고, target 속성은 당연하게 클릭 이벤트가 발생한 이미지 요소의 정보를 우리에게 알려준 것이었다.

     

    🙌 버튼에 대한 정보 받아오기

    이제 원인을 알았으니 다시 원하는 '버튼' 요소에 대한 정보를 받아오자. while 문을 활용하여 클릭한 요소의 class에 menu-btn이 없으면 해당 요소의 부모 요소를 찾도록 코드를 추가해 준다.

    <!-- 햄버거 버튼(X 버튼도 동일함)-->
     <button
      id="btn-open"
      type="button"
      data-toggle="open"
      class="text-black inline-flex items-center justify-center menu-btn"
    >
     <!-- svg 이미지 -->
    </button>
    // 클릭된 요소가 메뉴 버튼이 아니라면, 상위 요소 탐색
    // while문을 중지시킬 조건식을 넣지 않은 상태로 코드 실행시 무한 렌더링 걸리니 주의!
    while (elem && !elem.classList.contains("menu-btn")) {
    	elem = elem.parentNode; // 부모 요소가 햄버거 버튼이나 닫기 버튼이어야 함
    }

    현재 svg 태그의 부모는 button 태그이다.

     

    while문 조건을 통과하여 버튼 요소를 찾게 되면, toggle로 class를 수정하여 팝업 기능을 완성할 수 있다.

    if (elem && elem.classList.contains("menu-btn")) {
      menuBtnOpen.classList.toggle("hidden");
      menuBtnClose.classList.toggle("hidden");
      mobileMenu.classList.toggle("hidden");
    }

    적용 결과

     


    참고

    [사이트] 이벤트 위임 - 모던 JavaScript 튜토리얼(링크)

    [유튜브] 자바스크립트 이벤트 위임 핵심 정리 - 1분 코딩(링크)

    [블로그] event.target 이벤트가 발생된 요소 찾기 - jQuery(링크)

     

    💬 본 포스팅은 공부 기록용으로 정확하지 않은 정보가 존재할 수 있습니다. 발견하신다면 알려주세요!

     

Designed by Tistory.