chunk-038-slider.js

I’ll build this as a production-style standalone slider chunk for your RX Theme path, with no dependency on jQuery and safe guards so it does not break pages without a slider.

My dear friend, create this file:

assets/static-js/static-chunks-js/chunk-038-slider.js

This is an advanced vanilla JavaScript slider chunk for RX Theme. It supports autoplay, loop, arrows, dots, swipe, drag, keyboard, pause on hover, lazy image loading, accessibility, responsive slides per view, progress bar, thumbnails, custom events, and auto-initialization.

/*!
 * RX Theme Advanced Slider Chunk
 * File: assets/static-js/static-chunks-js/chunk-038-slider.js
 * Version: 1.0.0
 * Author: RX Theme
 *
 * Usage:
 * <div class="rx-slider" data-rx-slider data-autoplay="true" data-loop="true">
 *   <div class="rx-slider__viewport">
 *     <div class="rx-slider__track">
 *       <div class="rx-slider__slide">Slide 1</div>
 *       <div class="rx-slider__slide">Slide 2</div>
 *       <div class="rx-slider__slide">Slide 3</div>
 *     </div>
 *   </div>
 * </div>
 */

(function () {
  'use strict';

  const RX_SLIDER_VERSION = '1.0.0';
  const RX_SLIDER_SELECTOR = '[data-rx-slider]';
  const RX_SLIDER_INSTANCE_KEY = '__rxAdvancedSliderInstance';

  const defaults = {
    viewportSelector: '.rx-slider__viewport',
    trackSelector: '.rx-slider__track',
    slideSelector: '.rx-slider__slide',

    arrowPrevClass: 'rx-slider__arrow--prev',
    arrowNextClass: 'rx-slider__arrow--next',
    dotsClass: 'rx-slider__dots',
    dotClass: 'rx-slider__dot',
    dotActiveClass: 'is-active',

    activeClass: 'is-active',
    initializedClass: 'is-initialized',
    draggingClass: 'is-dragging',
    disabledClass: 'is-disabled',
    hiddenClass: 'is-hidden',

    perView: 1,
    gap: 0,
    startIndex: 0,
    loop: true,
    autoplay: false,
    autoplayDelay: 5000,
    autoplayDirection: 'next',

    speed: 450,
    easing: 'ease',
    draggable: true,
    swipe: true,
    keyboard: true,
    mousewheel: false,

    pauseOnHover: true,
    pauseOnFocus: true,
    stopOnInteraction: false,

    arrows: true,
    dots: true,
    pagination: true,
    progress: true,
    thumbnails: false,

    lazyLoad: true,
    autoHeight: false,
    adaptiveHeight: false,

    observeResize: true,
    observeMutation: false,
    intersectionPause: true,

    threshold: 50,
    dragThreshold: 0.18,

    breakpoints: {
      480: {
        perView: 1,
        gap: 12
      },
      768: {
        perView: 2,
        gap: 16
      },
      1024: {
        perView: 3,
        gap: 20
      }
    }
  };

  function toBool(value, fallback) {
    if (value === undefined || value === null || value === '') return fallback;
    if (typeof value === 'boolean') return value;
    return String(value).toLowerCase() === 'true';
  }

  function toNumber(value, fallback) {
    const num = Number(value);
    return Number.isFinite(num) ? num : fallback;
  }

  function clamp(value, min, max) {
    return Math.max(min, Math.min(value, max));
  }

  function prefersReducedMotion() {
    return window.matchMedia &&
      window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  }

  function mergeOptions(base, custom) {
    const output = Object.assign({}, base);

    Object.keys(custom || {}).forEach(function (key) {
      if (
        typeof custom[key] === 'object' &&
        custom[key] !== null &&
        !Array.isArray(custom[key])
      ) {
        output[key] = Object.assign({}, output[key] || {}, custom[key]);
      } else {
        output[key] = custom[key];
      }
    });

    return output;
  }

  function getDatasetOptions(root) {
    return {
      perView: toNumber(root.dataset.perView, defaults.perView),
      gap: toNumber(root.dataset.gap, defaults.gap),
      startIndex: toNumber(root.dataset.startIndex, defaults.startIndex),
      loop: toBool(root.dataset.loop, defaults.loop),
      autoplay: toBool(root.dataset.autoplay, defaults.autoplay),
      autoplayDelay: toNumber(root.dataset.autoplayDelay, defaults.autoplayDelay),
      autoplayDirection: root.dataset.autoplayDirection || defaults.autoplayDirection,
      speed: toNumber(root.dataset.speed, defaults.speed),
      draggable: toBool(root.dataset.draggable, defaults.draggable),
      swipe: toBool(root.dataset.swipe, defaults.swipe),
      keyboard: toBool(root.dataset.keyboard, defaults.keyboard),
      mousewheel: toBool(root.dataset.mousewheel, defaults.mousewheel),
      pauseOnHover: toBool(root.dataset.pauseOnHover, defaults.pauseOnHover),
      pauseOnFocus: toBool(root.dataset.pauseOnFocus, defaults.pauseOnFocus),
      stopOnInteraction: toBool(root.dataset.stopOnInteraction, defaults.stopOnInteraction),
      arrows: toBool(root.dataset.arrows, defaults.arrows),
      dots: toBool(root.dataset.dots, defaults.dots),
      progress: toBool(root.dataset.progress, defaults.progress),
      lazyLoad: toBool(root.dataset.lazyLoad, defaults.lazyLoad),
      autoHeight: toBool(root.dataset.autoHeight, defaults.autoHeight),
      adaptiveHeight: toBool(root.dataset.adaptiveHeight, defaults.adaptiveHeight)
    };
  }

  class RXAdvancedSlider {
    constructor(root, options) {
      if (!root) return;

      this.root = root;
      this.options = mergeOptions(defaults, options || {});
      this.options = mergeOptions(this.options, getDatasetOptions(root));

      this.viewport = this.root.querySelector(this.options.viewportSelector);
      this.track = this.root.querySelector(this.options.trackSelector);
      this.slides = [];

      this.index = this.options.startIndex || 0;
      this.previousIndex = this.index;

      this.slideWidth = 0;
      this.maxIndex = 0;
      this.perView = this.options.perView;
      this.gap = this.options.gap;

      this.isInitialized = false;
      this.isDragging = false;
      this.isPointerDown = false;
      this.isHovered = false;
      this.isFocused = false;
      this.isVisible = true;
      this.isDestroyed = false;

      this.startX = 0;
      this.currentX = 0;
      this.dragDelta = 0;
      this.dragStartTranslate = 0;

      this.autoplayTimer = null;
      this.resizeObserver = null;
      this.mutationObserver = null;
      this.intersectionObserver = null;

      this.dotsWrapper = null;
      this.progressWrapper = null;
      this.progressBar = null;
      this.prevButton = null;
      this.nextButton = null;

      this.bound = {
        onPrevClick: this.prev.bind(this),
        onNextClick: this.next.bind(this),
        onKeydown: this.onKeydown.bind(this),
        onMouseEnter: this.onMouseEnter.bind(this),
        onMouseLeave: this.onMouseLeave.bind(this),
        onFocusIn: this.onFocusIn.bind(this),
        onFocusOut: this.onFocusOut.bind(this),
        onPointerDown: this.onPointerDown.bind(this),
        onPointerMove: this.onPointerMove.bind(this),
        onPointerUp: this.onPointerUp.bind(this),
        onWheel: this.onWheel.bind(this),
        onResize: this.onResize.bind(this),
        onVisibilityChange: this.onVisibilityChange.bind(this)
      };

      this.init();
    }

    init() {
      if (!this.viewport || !this.track) return;
      if (this.root[RX_SLIDER_INSTANCE_KEY]) return;

      this.root[RX_SLIDER_INSTANCE_KEY] = this;

      this.collectSlides();

      if (!this.slides.length) return;

      this.applyAccessibility();
      this.applyResponsiveOptions();
      this.createControls();
      this.createDots();
      this.createProgress();

      this.updateLayout();
      this.goTo(this.index, false);

      this.bindEvents();
      this.observe();

      this.lazyLoadAround(this.index);
      this.updateState();

      this.root.classList.add(this.options.initializedClass);
      this.root.dataset.rxSliderReady = 'true';

      this.isInitialized = true;

      if (this.options.autoplay && !prefersReducedMotion()) {
        this.startAutoplay();
      }

      this.emit('rxSlider:init', {
        version: RX_SLIDER_VERSION,
        index: this.index,
        total: this.slides.length
      });
    }

    collectSlides() {
      this.slides = Array.prototype.slice.call(
        this.track.querySelectorAll(this.options.slideSelector)
      );

      this.slides.forEach((slide, i) => {
        slide.dataset.rxSlideIndex = String(i);
        slide.setAttribute('role', 'group');
        slide.setAttribute('aria-roledescription', 'slide');
        slide.setAttribute('aria-label', `${i + 1} of ${this.slides.length}`);
      });
    }

    applyAccessibility() {
      this.root.setAttribute('role', 'region');
      this.root.setAttribute('aria-roledescription', 'carousel');

      if (!this.root.getAttribute('aria-label')) {
        this.root.setAttribute('aria-label', 'RX content slider');
      }

      this.viewport.setAttribute('tabindex', '0');
      this.viewport.setAttribute('aria-live', this.options.autoplay ? 'off' : 'polite');
    }

    applyResponsiveOptions() {
      const width = window.innerWidth;
      let responsive = {};

      Object.keys(this.options.breakpoints || {})
        .map(Number)
        .sort((a, b) => a - b)
        .forEach((point) => {
          if (width >= point) {
            responsive = mergeOptions(responsive, this.options.breakpoints[point]);
          }
        });

      this.perView = toNumber(responsive.perView, this.options.perView);
      this.gap = toNumber(responsive.gap, this.options.gap);

      this.perView = Math.max(1, this.perView);
      this.gap = Math.max(0, this.gap);
    }

    createControls() {
      if (!this.options.arrows) return;

      this.prevButton = this.root.querySelector('[data-rx-slider-prev]');
      this.nextButton = this.root.querySelector('[data-rx-slider-next]');

      if (!this.prevButton) {
        this.prevButton = document.createElement('button');
        this.prevButton.type = 'button';
        this.prevButton.className = `rx-slider__arrow ${this.options.arrowPrevClass}`;
        this.prevButton.setAttribute('data-rx-slider-prev', '');
        this.prevButton.setAttribute('aria-label', 'Previous slide');
        this.prevButton.innerHTML = '<span aria-hidden="true">‹</span>';
        this.root.appendChild(this.prevButton);
      }

      if (!this.nextButton) {
        this.nextButton = document.createElement('button');
        this.nextButton.type = 'button';
        this.nextButton.className = `rx-slider__arrow ${this.options.arrowNextClass}`;
        this.nextButton.setAttribute('data-rx-slider-next', '');
        this.nextButton.setAttribute('aria-label', 'Next slide');
        this.nextButton.innerHTML = '<span aria-hidden="true">›</span>';
        this.root.appendChild(this.nextButton);
      }
    }

    createDots() {
      if (!this.options.dots) return;

      this.dotsWrapper = this.root.querySelector('[data-rx-slider-dots]');

      if (!this.dotsWrapper) {
        this.dotsWrapper = document.createElement('div');
        this.dotsWrapper.className = this.options.dotsClass;
        this.dotsWrapper.setAttribute('data-rx-slider-dots', '');
        this.dotsWrapper.setAttribute('role', 'tablist');
        this.dotsWrapper.setAttribute('aria-label', 'Slider pagination');
        this.root.appendChild(this.dotsWrapper);
      }

      this.renderDots();
    }

    renderDots() {
      if (!this.dotsWrapper) return;

      this.dotsWrapper.innerHTML = '';

      const dotCount = this.getPageCount();

      for (let i = 0; i < dotCount; i++) {
        const dot = document.createElement('button');
        dot.type = 'button';
        dot.className = this.options.dotClass;
        dot.setAttribute('data-rx-slider-dot', String(i));
        dot.setAttribute('role', 'tab');
        dot.setAttribute('aria-label', `Go to slide ${i + 1}`);
        dot.innerHTML = '<span></span>';

        dot.addEventListener('click', () => {
          this.interact();
          this.goTo(i);
        });

        this.dotsWrapper.appendChild(dot);
      }
    }

    createProgress() {
      if (!this.options.progress) return;

      this.progressWrapper = this.root.querySelector('[data-rx-slider-progress]');

      if (!this.progressWrapper) {
        this.progressWrapper = document.createElement('div');
        this.progressWrapper.className = 'rx-slider__progress';
        this.progressWrapper.setAttribute('data-rx-slider-progress', '');
        this.progressWrapper.innerHTML = '<span class="rx-slider__progress-bar"></span>';
        this.root.appendChild(this.progressWrapper);
      }

      this.progressBar = this.progressWrapper.querySelector('.rx-slider__progress-bar');
    }

    bindEvents() {
      if (this.prevButton) {
        this.prevButton.addEventListener('click', this.bound.onPrevClick);
      }

      if (this.nextButton) {
        this.nextButton.addEventListener('click', this.bound.onNextClick);
      }

      if (this.options.keyboard) {
        this.viewport.addEventListener('keydown', this.bound.onKeydown);
      }

      if (this.options.pauseOnHover) {
        this.root.addEventListener('mouseenter', this.bound.onMouseEnter);
        this.root.addEventListener('mouseleave', this.bound.onMouseLeave);
      }

      if (this.options.pauseOnFocus) {
        this.root.addEventListener('focusin', this.bound.onFocusIn);
        this.root.addEventListener('focusout', this.bound.onFocusOut);
      }

      if (this.options.draggable || this.options.swipe) {
        this.viewport.addEventListener('pointerdown', this.bound.onPointerDown);
        this.viewport.addEventListener('pointermove', this.bound.onPointerMove);
        this.viewport.addEventListener('pointerup', this.bound.onPointerUp);
        this.viewport.addEventListener('pointercancel', this.bound.onPointerUp);
        this.viewport.addEventListener('lostpointercapture', this.bound.onPointerUp);
      }

      if (this.options.mousewheel) {
        this.viewport.addEventListener('wheel', this.bound.onWheel, {
          passive: false
        });
      }

      window.addEventListener('resize', this.bound.onResize);
      document.addEventListener('visibilitychange', this.bound.onVisibilityChange);
    }

    observe() {
      if (this.options.observeResize && 'ResizeObserver' in window) {
        this.resizeObserver = new ResizeObserver(() => {
          this.onResize();
        });

        this.resizeObserver.observe(this.root);
        this.resizeObserver.observe(this.viewport);
      }

      if (this.options.observeMutation && 'MutationObserver' in window) {
        this.mutationObserver = new MutationObserver(() => {
          this.refresh();
        });

        this.mutationObserver.observe(this.track, {
          childList: true,
          subtree: false
        });
      }

      if (this.options.intersectionPause && 'IntersectionObserver' in window) {
        this.intersectionObserver = new IntersectionObserver((entries) => {
          entries.forEach((entry) => {
            this.isVisible = entry.isIntersecting;

            if (this.isVisible) {
              this.resumeAutoplay();
            } else {
              this.pauseAutoplay();
            }
          });
        }, {
          threshold: 0.15
        });

        this.intersectionObserver.observe(this.root);
      }
    }

    updateLayout() {
      this.applyResponsiveOptions();

      const viewportWidth = this.viewport.clientWidth;
      const totalGap = this.gap * (this.perView - 1);

      this.slideWidth = (viewportWidth - totalGap) / this.perView;
      this.maxIndex = Math.max(0, this.slides.length - this.perView);

      this.track.style.display = 'flex';
      this.track.style.gap = `${this.gap}px`;
      this.track.style.transition = `transform ${this.options.speed}ms ${this.options.easing}`;
      this.track.style.willChange = 'transform';

      this.slides.forEach((slide) => {
        slide.style.flex = `0 0 ${this.slideWidth}px`;
        slide.style.maxWidth = `${this.slideWidth}px`;
      });

      this.index = clamp(this.index, 0, this.maxIndex);

      if (this.dotsWrapper) {
        this.renderDots();
      }

      this.updateHeight();
      this.updateTransform(false);
      this.updateState();
    }

    updateTransform(animate = true) {
      if (!animate) {
        this.track.style.transition = 'none';
      } else {
        this.track.style.transition = `transform ${this.options.speed}ms ${this.options.easing}`;
      }

      const translate = this.getTranslateForIndex(this.index);
      this.track.style.transform = `translate3d(${translate}px, 0, 0)`;

      if (!animate) {
        this.track.offsetHeight;
        this.track.style.transition = `transform ${this.options.speed}ms ${this.options.easing}`;
      }
    }

    getTranslateForIndex(index) {
      return -index * (this.slideWidth + this.gap);
    }

    getPageCount() {
      if (!this.slides.length) return 0;
      return Math.max(1, this.maxIndex + 1);
    }

    goTo(targetIndex, animate = true) {
      if (!this.slides.length) return;

      this.previousIndex = this.index;

      let nextIndex = Number(targetIndex);

      if (this.options.loop) {
        if (nextIndex < 0) nextIndex = this.maxIndex;
        if (nextIndex > this.maxIndex) nextIndex = 0;
      } else {
        nextIndex = clamp(nextIndex, 0, this.maxIndex);
      }

      this.index = nextIndex;

      this.updateTransform(animate);
      this.lazyLoadAround(this.index);
      this.updateHeight();
      this.updateState();

      this.emit('rxSlider:change', {
        index: this.index,
        previousIndex: this.previousIndex,
        total: this.slides.length
      });
    }

    next() {
      this.interact();
      this.goTo(this.index + 1);
    }

    prev() {
      this.interact();
      this.goTo(this.index - 1);
    }

    first() {
      this.interact();
      this.goTo(0);
    }

    last() {
      this.interact();
      this.goTo(this.maxIndex);
    }

    updateState() {
      this.slides.forEach((slide, i) => {
        const isVisible = i >= this.index && i < this.index + this.perView;
        const isActive = i === this.index;

        slide.classList.toggle(this.options.activeClass, isActive);
        slide.setAttribute('aria-hidden', isVisible ? 'false' : 'true');

        const focusable = slide.querySelectorAll(
          'a, button, input, textarea, select, iframe, [tabindex]'
        );

        focusable.forEach((element) => {
          if (isVisible) {
            if (element.dataset.rxOldTabindex !== undefined) {
              element.setAttribute('tabindex', element.dataset.rxOldTabindex);
              delete element.dataset.rxOldTabindex;
            } else {
              element.removeAttribute('tabindex');
            }
          } else {
            if (!element.hasAttribute('data-rx-old-tabindex')) {
              element.dataset.rxOldTabindex = element.getAttribute('tabindex') || '';
            }

            element.setAttribute('tabindex', '-1');
          }
        });
      });

      if (this.prevButton) {
        const disabled = !this.options.loop && this.index === 0;
        this.prevButton.disabled = disabled;
        this.prevButton.classList.toggle(this.options.disabledClass, disabled);
        this.prevButton.setAttribute('aria-disabled', disabled ? 'true' : 'false');
      }

      if (this.nextButton) {
        const disabled = !this.options.loop && this.index >= this.maxIndex;
        this.nextButton.disabled = disabled;
        this.nextButton.classList.toggle(this.options.disabledClass, disabled);
        this.nextButton.setAttribute('aria-disabled', disabled ? 'true' : 'false');
      }

      this.updateDots();
      this.updateProgress();
    }

    updateDots() {
      if (!this.dotsWrapper) return;

      const dots = this.dotsWrapper.querySelectorAll('[data-rx-slider-dot]');

      dots.forEach((dot, i) => {
        const active = i === this.index;
        dot.classList.toggle(this.options.dotActiveClass, active);
        dot.setAttribute('aria-selected', active ? 'true' : 'false');
        dot.setAttribute('tabindex', active ? '0' : '-1');
      });
    }

    updateProgress() {
      if (!this.progressBar) return;

      const total = this.getPageCount();
      const percent = total <= 1 ? 100 : ((this.index + 1) / total) * 100;

      this.progressBar.style.width = `${percent}%`;
    }

    updateHeight() {
      if (!this.options.autoHeight && !this.options.adaptiveHeight) return;

      const visibleSlides = this.slides.slice(this.index, this.index + this.perView);
      let maxHeight = 0;

      visibleSlides.forEach((slide) => {
        maxHeight = Math.max(maxHeight, slide.offsetHeight);
      });

      if (maxHeight > 0) {
        this.viewport.style.height = `${maxHeight}px`;
      }
    }

    lazyLoadAround(index) {
      if (!this.options.lazyLoad) return;

      const from = Math.max(0, index - 1);
      const to = Math.min(this.slides.length - 1, index + this.perView + 1);

      for (let i = from; i <= to; i++) {
        this.lazyLoadSlide(this.slides[i]);
      }
    }

    lazyLoadSlide(slide) {
      if (!slide) return;

      const lazyItems = slide.querySelectorAll(
        'img[data-src], img[data-srcset], source[data-srcset], iframe[data-src]'
      );

      lazyItems.forEach((item) => {
        if (item.dataset.src) {
          item.setAttribute('src', item.dataset.src);
          delete item.dataset.src;
        }

        if (item.dataset.srcset) {
          item.setAttribute('srcset', item.dataset.srcset);
          delete item.dataset.srcset;
        }

        item.classList.add('is-loaded');
      });
    }

    startAutoplay() {
      this.stopAutoplay();

      if (!this.options.autoplay || prefersReducedMotion()) return;
      if (!this.isVisible || this.isHovered || this.isFocused) return;

      this.autoplayTimer = window.setInterval(() => {
        if (this.options.autoplayDirection === 'prev') {
          this.goTo(this.index - 1);
        } else {
          this.goTo(this.index + 1);
        }
      }, this.options.autoplayDelay);

      this.root.dataset.rxAutoplay = 'running';

      this.emit('rxSlider:autoplayStart', {
        delay: this.options.autoplayDelay
      });
    }

    stopAutoplay() {
      if (this.autoplayTimer) {
        window.clearInterval(this.autoplayTimer);
        this.autoplayTimer = null;
      }

      this.root.dataset.rxAutoplay = 'stopped';

      this.emit('rxSlider:autoplayStop', {});
    }

    pauseAutoplay() {
      if (!this.options.autoplay) return;
      this.stopAutoplay();
      this.root.dataset.rxAutoplay = 'paused';
    }

    resumeAutoplay() {
      if (!this.options.autoplay) return;
      if (this.isHovered || this.isFocused || !this.isVisible) return;
      this.startAutoplay();
    }

    interact() {
      if (this.options.stopOnInteraction) {
        this.stopAutoplay();
      }
    }

    onKeydown(event) {
      const key = event.key;

      if (key === 'ArrowLeft') {
        event.preventDefault();
        this.prev();
      }

      if (key === 'ArrowRight') {
        event.preventDefault();
        this.next();
      }

      if (key === 'Home') {
        event.preventDefault();
        this.first();
      }

      if (key === 'End') {
        event.preventDefault();
        this.last();
      }
    }

    onMouseEnter() {
      this.isHovered = true;
      this.pauseAutoplay();
    }

    onMouseLeave() {
      this.isHovered = false;
      this.resumeAutoplay();
    }

    onFocusIn() {
      this.isFocused = true;
      this.pauseAutoplay();
    }

    onFocusOut() {
      this.isFocused = false;
      this.resumeAutoplay();
    }

    onPointerDown(event) {
      if (!this.options.draggable && !this.options.swipe) return;
      if (event.button !== undefined && event.button !== 0) return;

      this.isPointerDown = true;
      this.isDragging = false;

      this.startX = event.clientX;
      this.currentX = event.clientX;
      this.dragDelta = 0;
      this.dragStartTranslate = this.getTranslateForIndex(this.index);

      this.viewport.setPointerCapture &&
        this.viewport.setPointerCapture(event.pointerId);

      this.track.style.transition = 'none';

      this.pauseAutoplay();

      this.emit('rxSlider:dragStart', {
        index: this.index
      });
    }

    onPointerMove(event) {
      if (!this.isPointerDown) return;

      this.currentX = event.clientX;
      this.dragDelta = this.currentX - this.startX;

      if (Math.abs(this.dragDelta) > 5) {
        this.isDragging = true;
        this.root.classList.add(this.options.draggingClass);
      }

      if (!this.options.loop) {
        if (this.index === 0 && this.dragDelta > 0) {
          this.dragDelta *= 0.35;
        }

        if (this.index === this.maxIndex && this.dragDelta < 0) {
          this.dragDelta *= 0.35;
        }
      }

      const nextTranslate = this.dragStartTranslate + this.dragDelta;
      this.track.style.transform = `translate3d(${nextTranslate}px, 0, 0)`;
    }

    onPointerUp(event) {
      if (!this.isPointerDown) return;

      this.isPointerDown = false;

      this.viewport.releasePointerCapture &&
        this.viewport.releasePointerCapture(event.pointerId);

      this.track.style.transition = `transform ${this.options.speed}ms ${this.options.easing}`;
      this.root.classList.remove(this.options.draggingClass);

      const movedEnough =
        Math.abs(this.dragDelta) > Math.max(this.options.threshold, this.slideWidth * this.options.dragThreshold);

      if (movedEnough) {
        if (this.dragDelta < 0) {
          this.goTo(this.index + 1);
        } else {
          this.goTo(this.index - 1);
        }
      } else {
        this.updateTransform(true);
      }

      this.isDragging = false;
      this.dragDelta = 0;

      this.resumeAutoplay();

      this.emit('rxSlider:dragEnd', {
        index: this.index
      });
    }

    onWheel(event) {
      if (!this.options.mousewheel) return;

      const absX = Math.abs(event.deltaX);
      const absY = Math.abs(event.deltaY);

      if (absX <= absY) return;

      event.preventDefault();

      if (event.deltaX > 0) {
        this.next();
      } else {
        this.prev();
      }
    }

    onResize() {
      window.requestAnimationFrame(() => {
        this.updateLayout();
      });
    }

    onVisibilityChange() {
      if (document.hidden) {
        this.pauseAutoplay();
      } else {
        this.resumeAutoplay();
      }
    }

    refresh() {
      this.collectSlides();
      this.applyResponsiveOptions();
      this.updateLayout();
      this.goTo(this.index, false);

      this.emit('rxSlider:refresh', {
        index: this.index,
        total: this.slides.length
      });
    }

    emit(name, detail) {
      const event = new CustomEvent(name, {
        bubbles: true,
        detail: detail || {}
      });

      this.root.dispatchEvent(event);
    }

    destroy() {
      if (this.isDestroyed) return;

      this.stopAutoplay();

      if (this.prevButton) {
        this.prevButton.removeEventListener('click', this.bound.onPrevClick);
      }

      if (this.nextButton) {
        this.nextButton.removeEventListener('click', this.bound.onNextClick);
      }

      this.viewport.removeEventListener('keydown', this.bound.onKeydown);

      this.root.removeEventListener('mouseenter', this.bound.onMouseEnter);
      this.root.removeEventListener('mouseleave', this.bound.onMouseLeave);
      this.root.removeEventListener('focusin', this.bound.onFocusIn);
      this.root.removeEventListener('focusout', this.bound.onFocusOut);

      this.viewport.removeEventListener('pointerdown', this.bound.onPointerDown);
      this.viewport.removeEventListener('pointermove', this.bound.onPointerMove);
      this.viewport.removeEventListener('pointerup', this.bound.onPointerUp);
      this.viewport.removeEventListener('pointercancel', this.bound.onPointerUp);
      this.viewport.removeEventListener('lostpointercapture', this.bound.onPointerUp);
      this.viewport.removeEventListener('wheel', this.bound.onWheel);

      window.removeEventListener('resize', this.bound.onResize);
      document.removeEventListener('visibilitychange', this.bound.onVisibilityChange);

      if (this.resizeObserver) {
        this.resizeObserver.disconnect();
      }

      if (this.mutationObserver) {
        this.mutationObserver.disconnect();
      }

      if (this.intersectionObserver) {
        this.intersectionObserver.disconnect();
      }

      this.track.removeAttribute('style');
      this.viewport.removeAttribute('style');

      this.slides.forEach((slide) => {
        slide.removeAttribute('style');
        slide.classList.remove(this.options.activeClass);
        slide.removeAttribute('aria-hidden');
      });

      this.root.classList.remove(this.options.initializedClass);
      delete this.root[RX_SLIDER_INSTANCE_KEY];

      this.isDestroyed = true;

      this.emit('rxSlider:destroy', {});
    }
  }

  function initRXSliders(context) {
    const scope = context || document;
    const sliders = scope.querySelectorAll(RX_SLIDER_SELECTOR);

    sliders.forEach((slider) => {
      if (!slider[RX_SLIDER_INSTANCE_KEY]) {
        new RXAdvancedSlider(slider);
      }
    });
  }

  function destroyRXSliders(context) {
    const scope = context || document;
    const sliders = scope.querySelectorAll(RX_SLIDER_SELECTOR);

    sliders.forEach((slider) => {
      if (slider[RX_SLIDER_INSTANCE_KEY]) {
        slider[RX_SLIDER_INSTANCE_KEY].destroy();
      }
    });
  }

  function refreshRXSliders(context) {
    const scope = context || document;
    const sliders = scope.querySelectorAll(RX_SLIDER_SELECTOR);

    sliders.forEach((slider) => {
      if (slider[RX_SLIDER_INSTANCE_KEY]) {
        slider[RX_SLIDER_INSTANCE_KEY].refresh();
      }
    });
  }

  window.RXAdvancedSlider = RXAdvancedSlider;

  window.RXSlider = {
    version: RX_SLIDER_VERSION,
    init: initRXSliders,
    refresh: refreshRXSliders,
    destroy: destroyRXSliders,
    getInstance: function (element) {
      return element ? element[RX_SLIDER_INSTANCE_KEY] : null;
    }
  };

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function () {
      initRXSliders(document);
    });
  } else {
    initRXSliders(document);
  }

  document.addEventListener('rxTheme:contentLoaded', function (event) {
    initRXSliders(event.detail && event.detail.container ? event.detail.container : document);
  });

})();

Use this basic HTML structure:

<section class="rx-slider" data-rx-slider data-autoplay="true" data-loop="true" data-per-view="1">
  <div class="rx-slider__viewport">
    <div class="rx-slider__track">
      <article class="rx-slider__slide">
        <h2>Slide One</h2>
        <p>RX Theme medical content slider.</p>
      </article>

      <article class="rx-slider__slide">
        <h2>Slide Two</h2>
        <p>Fast, accessible, SEO-friendly layout.</p>
      </article>

      <article class="rx-slider__slide">
        <h2>Slide Three</h2>
        <p>Works without jQuery.</p>
      </article>
    </div>
  </div>
</section>

Add this CSS later in your slider CSS file:

.rx-slider {
  position: relative;
  width: 100%;
  overflow: hidden;
}

.rx-slider__viewport {
  overflow: hidden;
  width: 100%;
}

.rx-slider__track {
  display: flex;
  will-change: transform;
}

.rx-slider__slide {
  min-width: 0;
}

.rx-slider__arrow {
  position: absolute;
  top: 50%;
  z-index: 5;
  transform: translateY(-50%);
  border: 0;
  cursor: pointer;
}

.rx-slider__arrow--prev {
  left: 12px;
}

.rx-slider__arrow--next {
  right: 12px;
}

.rx-slider__dots {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 16px;
}

.rx-slider__dot {
  width: 10px;
  height: 10px;
  border-radius: 999px;
  border: 0;
  cursor: pointer;
}

.rx-slider__dot.is-active {
  transform: scale(1.25);
}

.rx-slider__progress {
  width: 100%;
  height: 3px;
  overflow: hidden;
}

.rx-slider__progress-bar {
  display: block;
  height: 100%;
  width: 0;
  transition: width 300ms ease;
}

.rx-slider.is-dragging {
  cursor: grabbing;
}

.rx-slider.is-dragging .rx-slider__track {
  user-select: none;
}

For functions.php, enqueue it like this:

wp_enqueue_script(
    'rx-chunk-038-slider',
    get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-038-slider.js',
    array(),
    wp_get_theme()->get( 'Version' ),
    true
);

Best idea for RX Theme: keep this file only for slider functionality. Do not put unrelated header, menu, search, AJAX, or popup code inside this chunk. That will keep your theme faster and easier to debug.

Leave a Reply

Your email address will not be published. Required fields are marked *