엘리멘터 프로에서 제공하는 슬라이드 위젯은 각 슬라이드별로 컨텐츠의 색상과 위치를 변경하는 옵션을 제공합니다.
하지만 사이트 헤더가 슬라이드 위에 부유하는 경우, 슬라이드 배경과 헤더 요소(로고나 메뉴)의 명도가 비슷하면 시각적으로 문제가 생기는데, 이 글에서는 엘리멘터 슬라이드 배경에 따라 헤더 요소의 스타일을 변경하는 한가지 방안을 소개합니다.
- About Us
- Recruit
예시 슬라이드와 헤더의 생성
사실 이 기능은 Swiper
의 realIndexChange
이벤트를 쓰면 손쉽게 구현할 수 있는데, 문제는 엘리멘터 위젯에서 자동으로 생성한 Swiper 인스턴스에 접근하기가 까다롭다.
엘리멘터가 제공하는 블로그 문서도 오래됐고 불친절하며, 실제로 jQuery
가 Swiper
인스턴스를 생성하기전에 사용자의 코드가 먼저 초기화를 시도하는 문제가 있다.
이 글에서는 완전한 Step-by-step으로 설명하지는 않지만, 문제를 해결하는 최종 코드와 예시 템플릿 파일을 제공한다. 새로운 템플릿에서 실제 기능을 구현해 보고자 한다면 아래 내용을 준비한다.
- 새로운 템플릿에 슬라이드와 헤더,
HTML Widget
을 모두 감싸는wrapper
용 컨테이너를 생성한다. - 실제로는 글로벌 헤더 템플릿을 사용하더라도 지금은 예시용 헤더 컨테이너를 생성한다.
sync-header
아이디를 추가한다. - 헤더 컨테이너의
Position
을Absolute
로 변경한다. - 엘리멘터
Slides
위젯을 추가하고sync-slider
아이디를 추가한다. - 슬라이드 위젯의
z-index
를0
으로 변경한다. - 엘리멘터
HTML
위젯을 추가하고 문서의 마지막에 로드 되도록Navigator
패널을 이용해 최대한 하단으로 이동시킨다.
엘리멘터 HTML Widget으로 자바스크립트 작성
스크립트는 실시간으로 결과를 확인할 수 있도록 HTML Widget
을 사용해 페이지 내에서 Inline
으로 작성한다.
스크립트는 즉시실행 함수로 캡슐화하고 인스턴스를 생성할 때 3개의 옵션을 받아 처리한다.
- 슬라이드의 요소
- 슬라이드와 동기화 할 헤더 요소
- 동기화가 필요한 슬라이드 인덱스 번호
총 4개의 슬라이드가 있다면, 그 중 두 번째와 세 번째 슬라이드가 활성화 상태일 때는 헤더의 글자색을 바꾸기 위해 클래스를 추가하는 형식이다.
기본 골격은 인스턴스 호출, 생성자 함수, Swiper 초기화 함수, 요소 업데이트를 위한 함수로 나누어 볼 수 있겠다. 클래스나 프로토타입 체이닝 없이 생성자 함수에서 initSwiper.call(this)
를 사용해 초기화 함수에서 this
를 사용하도록 하였다.
당연하지만 indexForUpdate: [1, 2]
는 슬라이드의 두 번째와 세 번째를 선택한다. (0이 첫 번째 슬라이드이므로)
Swiper 인스턴스로의 접근
엘리멘터는 내부적으로 Swiper
를 사용하는 캐러셀 위젯의 래퍼 요소에 클래스를 추가하고, data()
메서드의 키로 해당 위젯의 인스턴스를 조회힌다.
하지만 엘리멘터 공식 블로그에서 제공하는 All Swiper.js Instances in Elementor are Now Exposed 문서가 너무 오래되었고 그동안 클래스와 변수 이름에도 변화가 생겨 접근이 쉽지 않다.
스크립트를 어디에 작성하고 실행할지에 따라 다르지만, 문서에서 제공하는 방법으로 접근하면 undefined
로 나오는 경우가 많다.
이 문제는 엘리멘터 버전 3.11
부터 도입 된 Swiper 라이브러리 업그레이드 기능의 추가와, jQuery
가 Swiper
인스턴스를 생성하는데 필요한 지연시간으로 인한 영향으로, 다음과 같이 추가 설정한다.
- 워드프레스 관리자 화면 → Elementor Settings → Features → Upgrade Swiper Library에서
Default
혹은Active
로 설정 - 아래 코드를 추가하여
Swiper
인스턴스를 비동기적으로 획득
전체 코드가 확장되었다. 32-48
라인은 jQuery
가 Swiper
인스턴스를 생성하고 나면 접근하기 위한 비동기 로직이다. 이 로직 없이 addEventListener
혹은 엘리멘터에서 제공하는 elementorFrontend.hooks.addAction
훅을 사용해도 인스턴스 생성 시기와 호출의 간극으로 인한 undefiend
가 발생할 수 있다.
17, 22
라인 역시 비동기 로직에 맞춰 수정하였다.
엘리멘터 슬라이드 배경에 따라 원격 요소 업데이트
사용자가 선택한 슬라이드 번호를 통해 슬라이더(swiper)와 헤더 요소에 active
클래스를 토글하는 함수를 추가했다.
- 비동기 로직에 따른
try-catch
문 추가 active
클래스를 토글하는 업데이트 함수 추가- 슬라이드가 변경 될 때 마다 업데이트 함수를 호출하기 위한
realIndexChange
이벤트 추가 - 클로저(closure)를 활용하여 클래스의 멤버 변수처럼
Swiper
인스턴스를 관리
이 시점에서 사용자가 지정한 슬라이드가 활성화 되면 슬라이더(swiper)와 헤더 요소에 active
클래스가 토글되고, CSS 스타일을 추가할 수 있다.
사용자가 지정한 슬라이드일 경우 헤더 상태 스타일링
헤더(#sync-header
) 요소의 Custom CSS
필드에 스타일을 추가한다 .active
클래스가 활성화 되었을 때를 표현하면 된다.
/*
* For sync with slides
*/
selector svg,
selector .elementor-widget-icon-list .elementor-icon-list-text {
transition: all .5s;
}
/* When selectd on a slider */
selector.active svg {
fill: #404040 !important;
}
selector.active .elementor-widget-icon-list .elementor-icon-list-text {
color: #404040;
}
사용자가 지정한 슬라이드일 경우 네비게이션과 페이지네이션 스타일링
엘리멘터 슬라이드 위젯은 콘트롤 패널에서 슬라이드 컨텐츠 스타일을 개별적으로 수정할 수 있지만 네비게이션과 페이지네이션은 공통 스타일을 사용하므로 이것도 스타일링이 필요하다.
슬라이더(#sync-slider
) 요소의 Custom CSS
필드에 스타일을 추가한다 .active
클래스가 활성화 되었을 때를 표현하면 된다.
/* Navigation customization */
selector .elementor-swiper-button i,
selector .elementor-swiper-button svg {
visibility: hidden;
}
selector .elementor-swiper-button {
border: 2px solid #fff;
border-right-width: 0;
border-bottom-width: 0;
transition: border .5s;
overflow: hidden;
}
selector .elementor-swiper-button-prev {
left: 30px;
transform: rotate(-45deg);
}
selector .elementor-swiper-button-next {
right: 30px;
transform: rotate(135deg);
}
/* Pagination customization */
selector .swiper .swiper-pagination-bullet {
background: #fff;
transition: border .5s;
}
/*
* For sync with slides
*/
/* Navigation when selectd on a slider */
selector .swiper.active .elementor-swiper-button {
border-color: #404040;
}
/* Pagination when selectd on a slider */
selector .swiper.active .swiper-pagination-bullet {
background: #404040;
opacity: .3;
}
selector .swiper.active .swiper-pagination-bullet-active {
opacity: 1;
}
네비게이션의 화살표 아이콘은 사용자가 변경할 수 있는 옵션을 제공하지 않는다. 기본 아이콘 모양새가 트렌드에 맞지 않으니 감추고, 단순하게 border
만으로 표현하도록 변경하였다.
슬라이드 위젯의 모든 기본 값은 흰 색인데 페이지네이션만 검은 색이다. 이 부분도 통일성을 주어 수정하였다.
슬라이드 인스턴스에서 모든 Swiper API 사용
엘리멘터 위젯에서 생성된 인스턴스를 성공적으로 가져왔으니 이제 모든 Swiper API
를 사용할 수 있다. Swiper
인스턴스는 비동기적으로 초기화되어 Promise를 통해 접근한다.
동기 기능이 필요한 슬라이드의 수 만큼 SyncSlides
함수의 인스턴스를 만들면 한 페이지 내에서 여러 슬라이드가 각각 독립적으로 다른 요소들과 동기할 수 있다.
리팩토링을 한다면, initSwiper()
함수에 위치한 realIndexChange
이벤트 부분을 새로 추가한 10
번 라인의 Promiose 비동기 로직 안쪽으로 이동시킬 수 있겠다.
- About Us
- Recruit
전체 코드
마치며
네비게이션과 페이지네이션을 완전히 커스텀하고 싶은 경우도 있다. 그러려면 슬라이드 위젯에서 자동으로 생성한 인스턴스의 파라미터와 이벤트를 모두 해제하고 추가 작업을 마친 후 인스턴스를 업데이트 해야하는데, 이 과정을 거치고 나면 위젯 콘트롤러와의 모든 이벤트 연결이 끊어져 사실상 위젯으로의 가치를 상실한다.
// Make slide instaces
const slideHero = new SyncSlides({
slideEl: '#sync-slider',
headerEl: '#sync-header',
indexForUpdate: [1],
prevEl: '.syncSlide-swiper-button-prev',
nextEl: '.syncSlide-swiper-button-next'
});
// Use any Swiper API methods & events
slideHero.initPromise.then(() => {
if (slideHero.swiper) {
// Override navigation elements
slideHero.swiper.params.navigation.prevEl = slideHero.options.prevEl;
slideHero.swiper.params.navigation.nextEl = slideHero.options.nextEl;
// Re-init navigation
slideHero.swiper.navigation.destroy();
slideHero.swiper.navigation.init();
slideHero.swiper.navigation.update();
}
});
대략 이러한 과정을 거쳐 기존의 인스턴스를 업데이트 한다