계발하는 개발자

[Swiper 에러] TypeError: el.removeEventListener is not a function 해결 본문

❗️Error

[Swiper 에러] TypeError: el.removeEventListener is not a function 해결

dev_genie 2023. 9. 22. 18:28

에러

 

원인

여러 상황에서의 테스트 + 나의 개인적인 추론으로는 setSwiper.destroy()로 setSwiper 인스턴스가 존재하는 경우 지웠다가, 아래에서 new Swiper()로 다시 생성할 때 이 과정에서 서로 다른 Swiper 객체가 참조되어  removeEventListener 함수로 지워져서 참조에러가 발생한 게 아닌가 싶다.

// swiper.js 
function btSwiper() {
    console.log("bt스와이퍼 작동중!")
    if (setSwiper) {
        // 이미 Swiper 인스턴스가 있으면 제거
        setSwiper.destroy();
    }
    
    setSwiper = new Swiper('.btSwiper', {
        spaceBetween: 20,
        slidesPerView: 4,
        resistanceRatio: 0, // 드래그시 저항 막기
        breakpoints: {
            // when window width is >= 900px
            900: {
                slidesPerView: 4,
            },
            // when window width is >= 700px
            700: {
                slidesPerView: 3,
            },
            // when window width is >= 600px
            600: {
                slidesPerView: 2,
            },
            // when window width is >= 300px
            300: {
                slidesPerView: 1,
            }
        }
    });
}

window.addEventListener('load', btSwiper);
window.addEventListener('resize', btSwiper);

window.addEventListener('beforeunload', function () {
    // 페이지 이동시 이벤트 리스너 제거
    window.removeEventListener('load', btSwiper);
    window.removeEventListener('resize', btSwiper);
});

 

해결

실제로 위 추론에 맞게 아래 부분 코드를 지우니까 더 이상의 참조에러는 발생하지 않았다.

if (setSwiper) {
    // 이미 Swiper 인스턴스가 있으면 제거
    setSwiper.destroy();
}

그러나 좀더 안전하게 동작시키기 위해 이벤트 핸들러(생성/제거) 코드를 컴포넌트 페이지로 옮겼다.

<!-- 메인페이지 best 섹션 템플릿 -->
<script>
// 공통기능함수
import crossMixin from "../../js/common.js";
// 공통 템플릿 데이터
import mainData from "../../js/tempData/mainComp.js";
// 제이쿼리 불러오기
import $ from "jquery";
// 스와이퍼 불러오기
import { btSwiper } from "../../js/swiper.js";
// 더미데이터
import { m_bestData } from "../../js/gdsData/mainData.js";

export default {
  name: "BestComp",
  mixins: [crossMixin],
  template: mainData.bestarea,
  data() {
    return {
      // 외부 더미 데이터
      m_bestData: m_bestData,
      notelnb: ["#1만원대 특가 신발", "#보부상 가방", "#썸머 슈즈"],
    };
  },
  methods: {
    moveDet(pm) {
        // 로컬스토리지에 데이터 저장
        localStorage.setItem("detnm", pm.name);

        // 페이지 이동
        this.$router.push({ path: "/product/all/best"}).then(() => {
            // 페이지 이동후 store 함수 실행!
            this.$store.dispatch("setDet");
        });
    }
  },
  mounted() {
    // 첫번째 dd에 강제 클릭
    $(".mbtit2 > dd:first").addClass("on").siblings().removeClass("on").trigger("click");
    const index = $(".mbtit2 > dd:first").index();
    this.setNum(index);

    // 클릭한 dd에만 on 넣기
    $(".mbtit2 > dd").click(function() {
      $(this).addClass("on").siblings().removeClass("on");
    });

    // 스와이퍼 호출
    btSwiper();
    // DOM 로드 & 리사이즈시 호출!
    window.addEventListener('load', btSwiper);
    window.addEventListener('resize', btSwiper);
  },
  // 컴포넌트 소멸 전 실행   
  beforeUnmount() {
    window.removeEventListener('load', btSwiper);
    window.removeEventListener('resize', btSwiper);
  },
};
</script>

 

그 결과, 현재까지 어떠한 참조에러도 발생하지 않는다!

 

배운점

위 기능 구현을 위해 다양한 각도로 버그가 발생하는지 따져보고, 해결을 했다.

load 됐을 때 함수 호출하니까 최초 호출하는 코드는 지웠다가 그러면 다른 페이지-> 현재 페이지로 이동해서 올 때 컴포넌트가 초기화 직전에 화면에 표시되어 버리는 시점 차이가 생겨서 셋팅한 값들이 적용이 안되는 것을 목격했다.(spaceBetween 속성 같은)

그래서 반드시 먼저는 위에서 직접 호출해준 다음에 load 되었을 때 다시 한 번 호출시켜야 한다는 걸 확실히 알게 되었다.

또한, beforeUnmount 훅(컴포넌트 소멸 직전 실행되는 훅)을 처음 써보며 그 역할에 대해 이해하게 되었다.

그리고 위와 같은 배움의 경험들로 한 가지의 기능 구현에도 다각도로 문제가 발생하지는 않는지 따져보고,

효과적인 에러 해결 방법을 고민해보게 된 것은 정말 값진 자산이다.

LIST
profile

dev_genie

@dev_genie

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!