A closer look
Screenshots
The useful part
What it does
DX Slider is a reusable, responsive carousel built with plain JavaScript and CSS. It supports multiple instances and keeps navigation, pagination, playback, accessibility state and slide animations in sync.
Transitions and motion
effect: slide, fade or scale; change it at setup or later withsetEffect()durationandeasing: control transition timinganimateElements,elementDurationandelementStagger: animate content inside the active slidedata-dx-animate: fade, fade-up, fade-down, fade-left, fade-right or zoom, with optional per-element delay and duration
Playback and navigation
autoplay,loop,startAtandstopOnInteractionpauseOnHoverandpauseOnFocus, plus automatic pause for dragging and hidden tabskeyboard,navigation,pagination,draggableandswipeThreshold
Layout, loading and integration
autoHeightfor slides with different content heightslazyLoadfor nearby images usingdata-dx-srcanddata-dx-srcsetonInit,onBeforeChange,onChangeandonDestroycallbacks- Custom init, before-change, change, effect-change and destroy events
- Public
next,previous,goTo,play,pause,setEffect,refresh,getStateanddestroymethods
Inspect and adapt
The code
The demo files are shown below. Download the complete package for the slider source, styles and demo.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DX Slider</title>
<link rel="stylesheet" href="dx-slider.css">
<link rel="stylesheet" href="demo.css">
</head>
<body>
<main class="demo-shell">
<header class="demo-intro">
<div>
<p class="demo-kicker">No dependencies</p>
<h1>DX Slider</h1>
<p>Responsive, swipeable and configurable for real projects.</p>
</div>
<div class="demo-effects" aria-label="Transition effect">
<span>Effect</span>
<button type="button" data-demo-effect="slide" aria-pressed="true">Slide</button>
<button type="button" data-demo-effect="fade" aria-pressed="false">Fade</button>
<button type="button" data-demo-effect="scale" aria-pressed="false">Scale</button>
</div>
</header>
<section class="dx-slider js-dx-slider" aria-label="Featured capabilities">
<div class="dx-viewport">
<div class="dx-track">
<article class="dx-slide dx-slide--mint" data-title="Responsive">
<div class="dx-slide__copy">
<span class="dx-slide__number" data-dx-animate="fade">01</span>
<p class="dx-slide__eyebrow" data-dx-animate="fade-up">Any screen</p>
<h2 data-dx-animate="fade-up">Built to fit.</h2>
<p data-dx-animate="fade-up">The track is measured again whenever the viewport changes.</p>
</div>
<div class="dx-art dx-art--frames" aria-hidden="true"><i></i><i></i><i></i></div>
</article>
<article class="dx-slide dx-slide--blue" data-title="Touch">
<div class="dx-slide__copy">
<span class="dx-slide__number" data-dx-animate="fade">02</span>
<p class="dx-slide__eyebrow" data-dx-animate="fade-right">Mouse + touch</p>
<h2 data-dx-animate="fade-right">Drag naturally.</h2>
<p data-dx-animate="fade-right">Horizontal intent is detected before the page scroll is interrupted.</p>
</div>
<div class="dx-art dx-art--swipe" aria-hidden="true"><i></i><b></b><span>← →</span></div>
</article>
<article class="dx-slide dx-slide--coral" data-title="Controls">
<div class="dx-slide__copy">
<span class="dx-slide__number" data-dx-animate="fade">03</span>
<p class="dx-slide__eyebrow" data-dx-animate="fade-up">Keyboard ready</p>
<h2 data-dx-animate="zoom">Every way in.</h2>
<p data-dx-animate="fade-up">Use arrow keys, Home, End, pagination, buttons or a swipe.</p>
</div>
<div class="dx-art dx-art--keys" aria-hidden="true"><i>←</i><i>→</i><i>home</i><i>end</i></div>
</article>
<article class="dx-slide dx-slide--gold" data-title="Autoplay">
<div class="dx-slide__copy">
<span class="dx-slide__number" data-dx-animate="fade">04</span>
<p class="dx-slide__eyebrow" data-dx-animate="fade-left">Considerate autoplay</p>
<h2 data-dx-animate="fade-left">Moves, then waits.</h2>
<p data-dx-animate="fade-left">Playback pauses for hover, focus, dragging and hidden browser tabs.</p>
</div>
<div class="dx-art dx-art--clock" aria-hidden="true"><i></i><b></b><span></span></div>
</article>
<article class="dx-slide dx-slide--violet" data-title="API">
<div class="dx-slide__copy">
<span class="dx-slide__number" data-dx-animate="fade">05</span>
<p class="dx-slide__eyebrow" data-dx-animate="fade-up">Public API</p>
<h2 data-dx-animate="fade-up">Make it yours.</h2>
<p data-dx-animate="fade-up">Navigate, control playback, switch effects, inspect state or destroy the instance.</p>
</div>
<pre class="dx-art dx-art--code" aria-hidden="true"><code>slider.goTo(3);
slider.setEffect('fade');
slider.pause();</code></pre>
</article>
</div>
</div>
<button class="dx-arrow dx-arrow--previous" type="button" data-dx-previous aria-label="Previous slide">←</button>
<button class="dx-arrow dx-arrow--next" type="button" data-dx-next aria-label="Next slide">→</button>
<footer class="dx-footer">
<div class="dx-count" aria-hidden="true">
<strong data-dx-current>01</strong><span></span><b data-dx-total>05</b>
</div>
<div class="dx-dots" data-dx-dots></div>
<button class="dx-play" type="button" data-dx-play aria-label="Pause autoplay"><i></i><span>Pause</span></button>
</footer>
<p class="dx-status" data-dx-status aria-live="polite" aria-atomic="true"></p>
</section>
<p class="demo-hint">Drag the slide, use the controls, or press the arrow keys.</p>
<div class="demo-examples">
<header>
<p class="demo-kicker">More setups</p>
<h2>Examples</h2>
<p>The same script with different options. View the page source to copy any of them.</p>
</header>
<section class="demo-example">
<header>
<h3>Fade, hands-free</h3>
<p>A crossfade that runs on its own: <code>effect: 'fade'</code> with <code>autoplay: 3500</code>. Hovering, focusing or hiding the tab pauses playback, and the play button stays in sync.</p>
</header>
<section class="dx-slider demo-compact" id="example-fade" aria-label="Fade with autoplay example">
<div class="dx-viewport">
<div class="dx-track">
<article class="dx-slide dx-slide--coral" data-title="Fade one">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">Fade</p>
<h3>Nothing moves sideways.</h3>
<p>Slides sit on top of each other and cross-dissolve.</p>
</div>
</article>
<article class="dx-slide dx-slide--blue" data-title="Fade two">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">Autoplay</p>
<h3>Every 3.5 seconds.</h3>
<p>The delay is the <code>autoplay</code> value in milliseconds.</p>
</div>
</article>
<article class="dx-slide dx-slide--violet" data-title="Fade three">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">Polite</p>
<h3>It knows when to stop.</h3>
<p>Hover this slide and the countdown quietly waits for you.</p>
</div>
</article>
</div>
</div>
<footer class="dx-footer">
<div class="dx-dots" data-dx-dots></div>
<button class="dx-play" type="button" data-dx-play aria-label="Pause autoplay"><i></i><span>Pause</span></button>
</footer>
<p class="dx-status" data-dx-status aria-live="polite" aria-atomic="true"></p>
</section>
</section>
<section class="demo-example">
<header>
<h3>Auto height</h3>
<p>With <code>autoHeight: true</code> the viewport animates to the height of the active slide, so short and long content can share one slider. <code>loop: false</code> gives the track hard ends and disables the arrows there.</p>
</header>
<section class="dx-slider demo-compact demo-autoheight" id="example-autoheight" aria-label="Auto height example">
<div class="dx-viewport">
<div class="dx-track">
<article class="dx-slide dx-slide--mint" data-title="Short">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">Short slide</p>
<h3>One line.</h3>
<p>This slide barely says anything.</p>
</div>
</article>
<article class="dx-slide dx-slide--gold" data-title="Medium">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">Medium slide</p>
<h3>A little more.</h3>
<p>This one has a couple of sentences, so it is naturally taller than the first. Watch the container ease to the new height instead of jumping.</p>
</div>
</article>
<article class="dx-slide dx-slide--blue" data-title="Long">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">Long slide</p>
<h3>The tall one.</h3>
<p>Auto height is made for content like this: a longer paragraph that would otherwise force every other slide to reserve the same space. The viewport measures the active slide and transitions its height using the same duration and easing as the slide animation, so the whole component feels consistent. Nothing below the slider jumps around unexpectedly — it simply eases out of the way.</p>
</div>
</article>
</div>
</div>
<button class="dx-arrow dx-arrow--previous" type="button" data-dx-previous aria-label="Previous slide">←</button>
<button class="dx-arrow dx-arrow--next" type="button" data-dx-next aria-label="Next slide">→</button>
<footer class="dx-footer">
<div class="dx-dots" data-dx-dots></div>
</footer>
<p class="dx-status" data-dx-status aria-live="polite" aria-atomic="true"></p>
</section>
</section>
<section class="demo-example">
<header>
<h3>Drag only, no chrome</h3>
<p>All controls removed: <code>navigation</code>, <code>pagination</code> and <code>keyboard</code> are off, leaving pure drag and swipe with <code>loop: false</code>. Useful inside card layouts where arrows would be noise.</p>
</header>
<section class="dx-slider demo-compact demo-bare" id="example-drag" aria-label="Drag only example">
<div class="dx-viewport">
<div class="dx-track">
<article class="dx-slide dx-slide--violet" data-title="Drag one">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">01 / 03</p>
<h3>Grab this slide.</h3>
<p>Drag with a mouse or swipe with a finger to move on.</p>
</div>
</article>
<article class="dx-slide dx-slide--coral" data-title="Drag two">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">02 / 03</p>
<h3>Halfway there.</h3>
<p>A short pull past <code>swipeThreshold</code> commits the change; anything less snaps back.</p>
</div>
</article>
<article class="dx-slide dx-slide--mint" data-title="Drag three">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">03 / 03</p>
<h3>The end of the track.</h3>
<p>With looping off, dragging further resists and settles back here.</p>
</div>
</article>
</div>
</div>
<p class="dx-status" data-dx-status aria-live="polite" aria-atomic="true"></p>
</section>
</section>
<section class="demo-example">
<header>
<h3>Remote control</h3>
<p>Any element on the page can drive a slider through its public API. These buttons live outside the component and call methods on the instance; the readout comes from <code>getState()</code>.</p>
</header>
<section class="dx-slider demo-compact demo-bare" id="example-remote" aria-label="Remote control example">
<div class="dx-viewport">
<div class="dx-track">
<article class="dx-slide dx-slide--gold" data-title="Remote one">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">External buttons</p>
<h3>Steered from outside.</h3>
<p>This slider has no built-in controls at all.</p>
</div>
</article>
<article class="dx-slide dx-slide--blue" data-title="Remote two">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">Programmatic</p>
<h3>Slide two.</h3>
<p>You arrived here through the API, not a swipe.</p>
</div>
</article>
<article class="dx-slide dx-slide--violet" data-title="Remote three">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">goTo(2)</p>
<h3>Jumped straight in.</h3>
<p>Indexes are zero-based, so this is slide number three.</p>
</div>
</article>
</div>
</div>
<p class="dx-status" data-dx-status aria-live="polite" aria-atomic="true"></p>
</section>
<div class="demo-remote" role="group" aria-label="External slider controls">
<button type="button" data-remote="previous">previous()</button>
<button type="button" data-remote="next">next()</button>
<button type="button" data-remote="goto">goTo(2)</button>
<button type="button" data-remote="play">play()</button>
<button type="button" data-remote="pause">pause()</button>
<span class="demo-remote__state" id="remote-readout" aria-live="polite"></span>
</div>
</section>
<section class="demo-example">
<header>
<h3>DOM events</h3>
<p>Every change dispatches bubbling DOM events on the root element, so other scripts can react without callbacks. This log listens for <code>dxsliderbeforechange</code> and <code>dxsliderchange</code>.</p>
</header>
<section class="dx-slider demo-compact" id="example-events" aria-label="DOM events example">
<div class="dx-viewport">
<div class="dx-track">
<article class="dx-slide dx-slide--blue" data-title="Events one">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">Listen in</p>
<h3>Everything is announced.</h3>
<p>Move this slider and watch the log below fill up.</p>
</div>
</article>
<article class="dx-slide dx-slide--mint" data-title="Events two">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">event.detail</p>
<h3>With context attached.</h3>
<p>Each event carries the previous index, the new index and the slide element.</p>
</div>
</article>
<article class="dx-slide dx-slide--coral" data-title="Events three">
<div class="dx-slide__copy">
<p class="dx-slide__eyebrow">Analytics ready</p>
<h3>Track it anywhere.</h3>
<p>Wire the same events into analytics, lazy loading or your own UI.</p>
</div>
</article>
</div>
</div>
<button class="dx-arrow dx-arrow--previous" type="button" data-dx-previous aria-label="Previous slide">←</button>
<button class="dx-arrow dx-arrow--next" type="button" data-dx-next aria-label="Next slide">→</button>
<footer class="dx-footer">
<div class="dx-dots" data-dx-dots></div>
</footer>
<p class="dx-status" data-dx-status aria-live="polite" aria-atomic="true"></p>
</section>
<ol class="demo-log" id="event-log" aria-label="Slider event log">
<li>Waiting for the first event…</li>
</ol>
</section>
</div>
</main>
<script src="dx-slider.js"></script>
<script>
var effectMatch = window.location.search.match(/[?&]effect=(slide|fade|scale)(?:&|$)/);
var initialEffect = effectMatch ? effectMatch[1] : 'slide';
var demoSlider = new DXSlider(document.querySelector('.js-dx-slider'), {
effect: initialEffect,
autoplay: 5000,
duration: 600,
easing: 'ease',
loop: true,
animateElements: true,
elementStagger: 110
});
var effectButtons = document.querySelectorAll('[data-demo-effect]');
var effectIndex;
for (effectIndex = 0; effectIndex < effectButtons.length; effectIndex += 1) {
effectButtons[effectIndex].setAttribute('aria-pressed', effectButtons[effectIndex].getAttribute('data-demo-effect') === initialEffect ? 'true' : 'false');
effectButtons[effectIndex].addEventListener('click', function () {
var buttonIndex;
demoSlider.setEffect(this.getAttribute('data-demo-effect'));
for (buttonIndex = 0; buttonIndex < effectButtons.length; buttonIndex += 1) {
effectButtons[buttonIndex].setAttribute('aria-pressed', effectButtons[buttonIndex] === this ? 'true' : 'false');
}
});
}
new DXSlider(document.getElementById('example-fade'), {
effect: 'fade',
autoplay: 3500,
duration: 700
});
new DXSlider(document.getElementById('example-autoheight'), {
autoHeight: true,
loop: false
});
new DXSlider(document.getElementById('example-drag'), {
navigation: false,
pagination: false,
keyboard: false,
loop: false
});
var remoteSlider = new DXSlider(document.getElementById('example-remote'), {
navigation: false,
pagination: false,
keyboard: false,
onChange: function () {
updateRemoteReadout(this);
}
});
function updateRemoteReadout(slider) {
var state = slider.getState();
document.getElementById('remote-readout').textContent = 'getState(): slide ' + (state.index + 1) + ' of ' + state.total + (state.playing ? ', playing' : ', paused');
}
var remoteButtons = document.querySelectorAll('[data-remote]');
var remoteIndex;
for (remoteIndex = 0; remoteIndex < remoteButtons.length; remoteIndex += 1) {
remoteButtons[remoteIndex].addEventListener('click', function () {
var action = this.getAttribute('data-remote');
if (action === 'goto') {
remoteSlider.goTo(2);
} else {
remoteSlider[action]();
}
updateRemoteReadout(remoteSlider);
});
}
updateRemoteReadout(remoteSlider);
var eventRoot = document.getElementById('example-events');
var eventLog = document.getElementById('event-log');
new DXSlider(eventRoot, { autoplay: 4200 });
function logSliderEvent(event) {
var detail = event.detail || {};
var row = document.createElement('li');
var text = event.type;
if (typeof detail.previousIndex === 'number') {
text += ' { previousIndex: ' + detail.previousIndex + ', index: ' + detail.index + ' }';
}
row.textContent = text;
eventLog.insertBefore(row, eventLog.firstChild);
while (eventLog.children.length > 5) {
eventLog.removeChild(eventLog.lastChild);
}
}
eventRoot.addEventListener('dxsliderbeforechange', logSliderEvent);
eventRoot.addEventListener('dxsliderchange', logSliderEvent);
</script>
</body>
</html>
* {
box-sizing: border-box;
}
html,
body {
min-height: 100%;
}
body {
margin: 0;
background: #edf2f5;
color: #1b2b45;
font-family: Arial, Helvetica, sans-serif;
}
button {
font: inherit;
}
.demo-shell {
width: 100%;
max-width: 1180px;
margin: 0 auto;
padding: 42px 30px 34px;
}
.demo-intro {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
margin: 0 0 26px;
-webkit-box-align: end;
-ms-flex-align: end;
align-items: flex-end;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
}
.demo-kicker,
.demo-intro h1,
.demo-intro p {
margin-top: 0;
}
.demo-kicker {
margin-bottom: 8px;
color: #15965b;
font-size: 12px;
font-weight: bold;
letter-spacing: 2px;
text-transform: uppercase;
}
.demo-intro h1 {
margin-bottom: 8px;
font-size: 34px;
line-height: 1;
}
.demo-intro > div > p:last-child {
margin-bottom: 0;
color: #607089;
}
.demo-effects {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
padding-bottom: 2px;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.demo-effects span {
margin-right: 10px;
color: #718096;
font-size: 11px;
font-weight: bold;
letter-spacing: 1px;
text-transform: uppercase;
}
.demo-effects button {
min-width: 62px;
margin-left: 5px;
padding: 8px 11px;
border: 1px solid #c9d4dd;
background: #fff;
color: #4d5f75;
font-size: 12px;
font-weight: bold;
cursor: pointer;
}
.demo-effects button:hover,
.demo-effects button:focus,
.demo-effects button[aria-pressed='true'] {
border-color: #15965b;
background: #5fd591;
color: #173024;
outline: 0;
}
.demo-hint {
margin: 20px 0 0;
color: #718096;
font-size: 13px;
text-align: center;
}
/* Stacked example sections */
.demo-examples {
margin-top: 74px;
}
.demo-examples > header h2 {
margin: 0 0 8px;
font-size: 27px;
line-height: 1;
}
.demo-examples > header p {
margin: 0;
color: #607089;
}
.demo-example {
margin-top: 46px;
}
.demo-example > header {
max-width: 760px;
margin-bottom: 14px;
}
.demo-example > header h3 {
margin: 0 0 6px;
font-size: 18px;
}
.demo-example > header p {
margin: 0;
color: #607089;
font-size: 14px;
line-height: 1.55;
}
.demo-example code {
padding: 1px 5px;
background: #dde6ec;
color: #1b2b45;
font-family: Consolas, Menlo, monospace;
font-size: 13px;
}
/* Compact slider variant used by the examples */
.demo-compact .dx-slide {
min-height: 260px;
padding: 42px 8% 100px;
}
.demo-compact .dx-slide__copy {
width: 100%;
max-width: 640px;
}
.demo-compact .dx-slide h3 {
margin: 0 0 12px;
color: #fff;
font-size: 30px;
line-height: 1.05;
letter-spacing: -1px;
}
.demo-compact .dx-slide__copy > p:last-child {
font-size: 15px;
}
.demo-compact .dx-slide code {
padding: 1px 5px;
background: rgba(255, 255, 255, .14);
color: #fff;
font-size: 13px;
}
.demo-compact .dx-footer {
height: 56px;
padding: 0 18px;
}
/* Examples without a footer bar */
.demo-bare .dx-slide {
min-height: 220px;
padding-bottom: 46px;
}
/* Auto height example: let slides take their natural height */
.demo-autoheight .dx-track {
-webkit-box-align: start;
-ms-flex-align: start;
align-items: flex-start;
}
.demo-autoheight .dx-slide {
min-height: 0;
}
/* Remote control example */
.demo-remote {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.demo-remote button {
margin: 10px 8px 0 0;
padding: 8px 12px;
border: 1px solid #c9d4dd;
background: #fff;
color: #4d5f75;
font-family: Consolas, Menlo, monospace;
font-size: 12px;
font-weight: bold;
cursor: pointer;
}
.demo-remote button:hover,
.demo-remote button:focus {
border-color: #15965b;
background: #5fd591;
color: #173024;
outline: 0;
}
.demo-remote__state {
margin: 10px 0 0 6px;
color: #607089;
font-family: Consolas, Menlo, monospace;
font-size: 12px;
}
/* DOM events example */
.demo-log {
margin: 12px 0 0;
padding: 14px 18px;
list-style: none;
background: #1b2b45;
color: #9fb1c9;
font-family: Consolas, Menlo, monospace;
font-size: 12px;
line-height: 1.7;
}
.demo-log li:first-child {
color: #5fd591;
}
@media (max-width: 760px) {
.demo-shell {
padding: 28px 16px;
}
.demo-intro {
display: block;
}
.demo-intro h1 {
font-size: 29px;
}
.demo-effects {
margin-top: 18px;
padding-bottom: 0;
}
.demo-examples {
margin-top: 54px;
}
.demo-example {
margin-top: 38px;
}
.demo-compact .dx-slide {
min-height: 240px;
padding: 30px 18px 96px;
}
.demo-compact .dx-slide h3 {
font-size: 25px;
}
.demo-bare .dx-slide {
min-height: 200px;
padding-bottom: 38px;
}
.demo-autoheight .dx-slide {
min-height: 0;
}
}
From zero to working
Install & use
- 01
Copy dx-slider.css and dx-slider.js into your project and load both on the page.
- 02
Use the HTML structure from the demo: one .dx-viewport containing a .dx-track and one or more .dx-slide elements.
- 03
Add optional controls with the data-dx-previous, data-dx-next, data-dx-dots, data-dx-play, data-dx-current, data-dx-total and data-dx-status attributes.
- 04
Add data-dx-animate to slide content that should animate when its slide becomes active. Use data-dx-delay and data-dx-duration for individual timing.
- 05
Create an instance with new DXSlider(element, options). Set effect to slide, fade or scale, and set autoplay to a delay in milliseconds or 0 to disable it.
- 06
Keep each slider instance if you need to call its public methods or destroy it later.
Try it first
Live demo
Know before using
Caveats & FAQ
- The component displays one slide at a time; multi-column carousels need different sizing and movement calculations.
- Slides are collected during setup. Destroy and create the instance again after adding or removing slides.
- Loop mode clones the first and last slides. Avoid forms, video players or other stateful controls inside those boundary slides.
No. The slider has no runtime dependencies.
Yes. Pass effect: "fade" or effect: "scale" during setup, or call slider.setEffect("fade") at runtime.
Add data-dx-animate="fade-up" (or fade, fade-down, fade-left, fade-right or zoom) to any element inside a slide. DX Slider replays those animations when the slide becomes active.
Yes. Create a separate DXSlider instance for each root element.
Leave autoplay at its default value of 0, or pass autoplay: 0 when creating the instance.
Yes. Listen for the dxsliderchange event or use the onChange callback. Both receive the active index and slide element.
The contract
Options & API
Options
| Name | Type | Default | Description |
|---|---|---|---|
effect | string | 'slide' | Transition effect: 'slide', 'fade' or 'scale'. |
duration | number | 600 | Slide transition duration in milliseconds. |
easing | string | 'ease' | CSS easing function used for transitions. |
autoplay | number | 0 | Delay between automatic advances in milliseconds. 0 disables autoplay. |
loop | boolean | true | Loop seamlessly past the first and last slide using boundary clones. |
startAt | number | 0 | Zero-based index of the slide shown first. |
keyboard | boolean | true | Move between slides with the arrow keys while the slider has focus. |
navigation | boolean | true | Wire up elements with data-dx-previous and data-dx-next as arrow controls. |
pagination | boolean | true | Build dot pagination inside the data-dx-dots element. |
draggable | boolean | true | Change slides by dragging with a mouse, pen or touch. |
swipeThreshold | number | 50 | Distance in pixels a drag must travel before the slide changes. |
pauseOnHover | boolean | true | Pause autoplay while the pointer is over the slider. |
pauseOnFocus | boolean | true | Pause autoplay while focus is inside the slider. |
stopOnInteraction | boolean | false | Stop autoplay for good after the visitor navigates manually, instead of resuming. |
autoHeight | boolean | false | Animate the viewport height to match the active slide. |
lazyLoad | boolean | true | Load data-dx-src images only when their slide is active or adjacent. |
animateElements | boolean | true | Replay data-dx-animate content animations when a slide becomes active. |
elementDuration | number | 600 | Duration of element animations in milliseconds. |
elementStagger | number | 100 | Delay in milliseconds between each animated element in a slide. |
Methods
| Name | Type | Default | Description |
|---|---|---|---|
next() | Advance to the next slide. | ||
previous() | Go back to the previous slide. | ||
goTo(index, interaction) | Go to a zero-based slide index. Pass true as interaction to count it as a manual action for stopOnInteraction. | ||
play() | Start or resume autoplay. | ||
pause() | Pause autoplay until play() is called. | ||
setEffect(effect) | Switch to 'slide', 'fade' or 'scale' at runtime. | ||
update(announce) | Recalculate sizes and positions after a layout change. | ||
getState() | Return the current index, slide element and slide count. | ||
destroy() | Remove listeners, clones and generated markup, restoring the original element. |
Events
| Name | Type | Default | Description |
|---|---|---|---|
onInit | callback | null | Runs once the slider is ready. Receives the initial state. |
onBeforeChange | callback | null | Runs before a transition starts. Receives the previous and target index. |
onChange | callback | null | Runs after a transition completes. Receives the active index and slide element. |
onDestroy | callback | null | Runs after destroy() finishes. |
dxsliderinit | DOM event | Fired on the root element when the slider is ready. | |
dxsliderbeforechange | DOM event | Fired before a transition starts, with the indexes in event.detail. | |
dxsliderchange | DOM event | Fired after a transition completes, with the state in event.detail. | |
dxslidereffectchange | DOM event | Fired when the effect is switched at runtime. | |
dxsliderdestroy | DOM event | Fired on the root element after teardown. |
Release notes
Changelog
- v1.0.3
Refined inactive-slide focus handling and screen-reader announcements; fixed teardown so author attributes and inline styles are restored cleanly.
- v1.0.2
Improved adjacent-slide lazy loading and corrected auto-height behaviour for fade, scale and resized sliders.
- v1.0.1
Smoothed horizontal drag intent, tightened autoplay timer resets and fixed boundary-clone content animations during seamless looping.
- v1.0.0
Initial release with slide, fade and scale effects; element animations; looping; drag and touch gestures; autoplay; keyboard controls; pagination; lazy loading; auto-height; accessibility state; callbacks; custom events; runtime controls and complete teardown.
Why it exists
Era & lineage
This started as a need for a simple slider that stays understandable when the feature list grows. The component keeps its structure explicit, small, exposes a small public API and can be dropped into a page without bringing along a framework or plugin.

Feedback
Comments
No comments yet. Found a bug or used it somewhere interesting? Say so below.