본문 바로가기

JavaScript

[Swiper.js] 복제 DOM 이벤트 부착 시점의 문제

Swiper를 사용할 때, autoplay 속성을 활성화하면 슬라이드 간의 자연스러운 움직임을 나타내기 위해서 원본 DOM이 앞 뒤로 복제된다. 그래서 실제로 내가 타겟으로 삼은 DOM 요소들은 중앙에서 나타나고, Swiper 옵션이 적용됨과 동시에 앞 뒤로 duplicate 라는 이름의 클래스를 가진 복제 DOM들이 자동으로 생성된 것을 확인할 수 있다. (그래서 슬라이드가 전환될 때마다 activeIndex, realIndex를 찍어보면 본래 의도한 순서와 다르게 실제 의도한 수 + 총 원본 갯수 의 수로 나타난다.)

 

이 때 중요한 것은 원본 객체의 이벤트 리스너 부착 시점이다. 예를 들어 제품 정보를 나타내는 영역이었다고 가정해보자. 하나의 제품 정보에는 장바구니 담기, 좋아요 버튼, 제품 상세 페이지로 이동 등의 클릭 이벤트가 할당된다.

 

그런데 만약 이 이벤트 리스너 할당 로직이 swiper 객체보다 먼저 들어온다면 버그가 발생할 수 있다. 왜냐하면, 원본 DOM에 이벤트 리스너가 바인딩되어 있는 상태여도, 그 원본을 복제한 DOM은 요소의 구조만 복제할 뿐, 바인딩된 이벤트 리스너까지 복제하지는 않기 때문이다.

 

따라서 위의 로직으로 수행된 결과는 이렇다. Swiper가 첫 번째 순회를 마치고 돌아오는 순간부터 개발자가 의도했던 index (원본 index) 와 순서가 틀어지게 되면서, 일부 또는 전체 상품의 클릭 이벤트 동작이 먹히지 않게 된다.

 

위 문제에 대한 해결 방법은 여러가지가 있다.

가장 좋은 방법은 순서가 보장되는 로직을 적용하는 것이고 (DOM이 복제되고 난 다음에 이벤트 리스너를 할당)

차선책은 (원본에 이벤트 리스너가 부착되고, 그 다음에 DOM이 복제된 상황) 해당 영역의 이벤트를 전부 replace 처리하고, 전체에 이벤트 리스너를 다시 바인딩 하는 것이다.

차선책의 방법으로는 jQuery의 unbind()를 사용하면 편한데, 만약 바닐라JS를 사용하고 있다면 다음과 같이 직접 정의해서 사용하도록 하자.

HTMLElement.prototype.unbind = function (event) {
    const newElement = this.cloneNode(true);
    this.parentNode.replaceChild(newElement, this);
    return newElement; // 새로운 요소 반환
};

// 사용 예제
const button = document.querySelector("button");
const newButton = button.unbind(); // 모든 이벤트 제거됨