<p>The tiniest <a href="https://getbutterfly.com/how-to-build-a-vanilla-javascript-slider-in-less-than-100-lines/">Vanilla JavaScript Sliders</a>.</p>
<div class="wrap">
<div class="swipe">
<ul id="slider4">
<li style="display:block"><img src="https://images.unsplash.com/photo-1533667586627-9f5ddbd42539?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=bb707c0b87345277ebc415156111fb69&auto=format&fit=crop&w=1350&q=80" alt=""></li>
<li><img src="https://images.unsplash.com/photo-1519643381401-22c77e60520e?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=5a01717a33db39cbdabd468904b61d22&auto=format&fit=crop&w=1353&q=80" alt=""></li>
<li><img src="https://images.unsplash.com/photo-1519302959554-a75be0afc82a?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=249031677221bcb99a1fe56668c5c97b&auto=format&fit=crop&w=1362&q=80" alt=""></li>
<li><img src="https://images.unsplash.com/photo-1517079495967-03aed29f98b5?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=eb42f335f56f42f2abe66f4e6c2023b9&auto=format&fit=crop&w=1350&q=80" alt=""></li>
</ul>
</div>
<br>
<div id="pagenavi">
<a href="#" class="active">1</a>
<a href="#">2</a>
<a href="#">3</a>
<a href="#">4</a>
</div>
<br>
<button class="btn btn-primary" onclick="t4.pause();">Pause</button>
<button class="btn btn-primary" onclick="t4.play();">Play</button>
<button class="btn btn-primary" onclick="t4.prev();">Previous</button>
<button class="btn btn-primary" onclick="t4.next();">Next</button>
</div>
<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CESDK2J7&placement=getbutterflycom" id="_carbonads_js"></script>
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Segoe UI Symbol", "Apple Color Emoji", "Segoe UI Emoji";
font-size: 14px;
line-height: 1.5;
color: #24292e;
background-color: #fff;
}
input,
button {
font-family: inherit;
font-size: inherit;
}
.wrap {
margin: 0 auto;
padding: 0;
width: 600px;
}
.swipe {
margin: 0 auto;
overflow: hidden;
position: relative;
width: 100%;
}
.swipe ul {
overflow: hidden;
margin: 0;
padding: 0;
}
.swipe li div,
.swipe div div div {
padding: 50px 10px;
background: #1db1ff;
font-weight: bold;
color: #fff;
font-size: 20px;
text-align: center;
}
.swipe li img {
width: 100%;
}
.swipe li {
margin: 0;
padding: 0;
}
#slider2 li {
width: 230px;
}
#slider4 ul {
transition: left 800ms ease-in 0;
}
#pagenavi a {
display: inline-block;
width: 80px;
height: 60px;
background-color: yellow;
}
#pagenavi a.active {
color: red;
}
(function(ROOT, struct, undefined) {
"use strict";
var lastTime = 0,
nextFrame =
ROOT.requestAnimationFrame ||
ROOT.webkitRequestAnimationFrame ||
ROOT.mozRequestAnimationFrame ||
ROOT.msRequestAnimationFrame ||
function(callback) {
var currTime = +new Date(),
delay = Math.max(
1000 / 60,
1000 / 60 - (currTime - lastTime)
);
lastTime = currTime + delay;
return setTimeout(callback, delay);
},
cancelFrame =
ROOT.cancelAnimationFrame ||
ROOT.webkitCancelAnimationFrame ||
ROOT.webkitCancelRequestAnimationFrame ||
ROOT.mozCancelRequestAnimationFrame ||
ROOT.msCancelRequestAnimationFrame ||
clearTimeout,
DOC = ROOT.document,
divstyle = DOC.createElement("div").style,
cssVendor = (function() {
var tests = "-webkit- -moz- -o- -ms-".split(" "),
prop;
while ((prop = tests.shift())) {
if (camelCase(prop + "transform") in divstyle) {
return prop;
}
}
return "";
})(),
transition = cssTest("transition"),
toString = Object.prototype.toString,
slice = [].slice,
class2type = {},
event2type = {},
event2code = {
click: 4,
dommousescroll: 5,
keydown: 6,
resize: 7
},
POINTERTYPES = {
2: "touch",
3: "pen",
4: "mouse",
pen: "pen"
},
STARTEVENT = [],
MOVEEVENT = [],
EVENT = (function() {
var ret = {},
states = {
start: 1,
down: 1,
move: 2,
end: 3,
up: 3,
cancel: 3
};
each("mouse touch pointer MSPointer-".split(" "), function(prefix) {
var _prefix = /pointer/i.test(prefix) ? "pointer" : prefix;
ret[_prefix] = ret[_prefix] || {};
POINTERTYPES[_prefix] = _prefix;
each(states, function(endfix, code) {
var ev = camelCase(prefix + endfix);
ret[_prefix][ev] = code;
event2type[ev.toLowerCase()] = _prefix;
event2code[ev.toLowerCase()] = code;
if (code == 1) {
STARTEVENT.push(ev);
} else {
MOVEEVENT.push(ev);
}
});
});
each(
"otransitionend oTransitionEnd webkitTransitionEnd mozTransitionEnd MSTransitionEnd transitionend".split(
" "
),
function(ev) {
STARTEVENT.push(ev);
event2code[ev.toLowerCase()] = 8;
}
);
return ret;
})(),
POINTERS = {
touch: {},
pointer: {},
mouse: {}
};
each(
"Boolean Number String Function Array Date RegExp Object Error".split(
" "
),
function(name) {
class2type["[object " + name + "]"] = name.toLowerCase();
}
);
function type(obj) {
if (obj == null) {
return obj + "";
}
return typeof obj == "object" || typeof obj == "function"
? class2type[toString.call(obj)] || "object"
: typeof obj;
}
function isArrayLike(elem) {
var tp = type(elem);
return (
!!elem &&
tp != "function" &&
tp != "string" &&
(elem.length === 0 ||
(elem.length &&
(elem.nodeType == 1 || elem.length - 1 in elem)))
);
}
function camelCase(str) {
return (str + "")
.replace(/^-ms-/, "ms-")
.replace(/-([a-z]|[0-9])/gi, function(all, letter) {
return (letter + "").toUpperCase();
});
}
function cssTest(name) {
var prop = camelCase(name),
_prop = camelCase(cssVendor + prop);
return (prop in divstyle && prop) || (_prop in divstyle && _prop) || "";
}
function isFunction(func) {
return type(func) == "function";
}
function pointerLength(obj) {
var len = 0,
key;
if (type(obj.length) == "number") {
len = obj.length;
} else if ("keys" in Object) {
len = Object.keys(obj).length;
} else {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
len++;
}
}
}
return len;
}
function pointerItem(obj, n) {
return "item" in obj
? obj.item(n)
: function() {
var i = 0,
key;
for (key in this) {
if (i++ == n) {
return this[key];
}
}
}.call(obj, n);
}
function each(arr, iterate) {
if (isArrayLike(arr)) {
if (type(arr.forEach) == "function") {
return arr.forEach(iterate);
}
var i = 0,
len = arr.length,
item;
for (; i < len; i++) {
item = arr[i];
if (type(item) != "undefined") {
iterate(item, i, arr);
}
}
} else {
var key;
for (key in arr) {
iterate(key, arr[key], arr);
}
}
}
function children(elem) {
var ret = [];
each(elem.children || elem.childNodes, function(elem) {
if (elem.nodeType == 1) {
ret.push(elem);
}
});
return ret;
}
function getStyle(elem, prop) {
var style =
(ROOT.getComputedStyle && ROOT.getComputedStyle(elem, null)) ||
elem.currentStyle ||
elem.style;
return style[prop];
}
function setStyle(elem, props) {
each(props, function(name, value) {
var prop;
switch (name) {
case "float":
prop = cssTest("cssFloat") ? "cssFloat" : "styleFloat";
break;
default:
prop = camelCase(name);
}
try {
elem.style[prop] = value;
} catch (e) {}
});
}
function addListener(elem, evstr, handler) {
if (type(evstr) == "object") {
return each(evstr, function(evstr, handler) {
addListener(elem, evstr, handler);
});
}
each(evstr.split(" "), function(ev) {
if (elem.addEventListener) {
elem.addEventListener(ev, handler, false);
} else if (elem.attachEvent) {
elem.attachEvent("on" + ev, handler);
} else elem["on" + ev] = handler;
});
}
function offListener(elem, evstr, handler) {
if (type(evstr) == "object") {
return each(evstr, function(evstr, handler) {
offListener(elem, evstr, handler);
});
}
each(evstr.split(" "), function(ev) {
if (elem.removeEventListener) {
elem.removeEventListener(ev, handler, false);
} else if (elem.detachEvent) {
elem.detachEvent("on" + ev, handler);
} else elem["on" + ev] = null;
});
}
function removeRange() {
var range;
if (ROOT.getSelection) {
range = getSelection();
if ("empty" in range) range.empty();
else if ("removeAllRanges" in range) range.removeAllRanges();
} else {
DOC.selection.empty();
}
}
function EASE(t, b, c, d) {
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
}
function filterEvent(oldEvent) {
var ev = {},
which = oldEvent.which,
button = oldEvent.button,
pointers,
pointer;
each("wheelDelta detail which keyCode".split(" "), function(prop) {
ev[prop] = oldEvent[prop];
});
ev.oldEvent = oldEvent;
ev.type = oldEvent.type.toLowerCase();
ev.eventType = event2type[ev.type] || ev.type;
ev.eventCode = event2code[ev.type] || 0;
ev.pointerType =
POINTERTYPES[oldEvent.pointerType] ||
oldEvent.pointerType ||
ev.eventType;
ev.target =
oldEvent.target || oldEvent.srcElement || DOC.documentElement;
if (ev.target.nodeType === 3) {
ev.target = ev.target.parentNode;
}
ev.preventDefault = function() {
oldEvent.preventDefault && oldEvent.preventDefault();
ev.returnValue = oldEvent.returnValue = false;
};
if ((pointers = POINTERS[ev.eventType])) {
switch (ev.eventType) {
case "mouse":
case "pointer":
var id = oldEvent.pointerId || 0;
ev.eventCode == 3
? delete pointers[id]
: (pointers[id] = oldEvent);
break;
case "touch":
POINTERS[ev.eventType] = pointers = oldEvent.touches;
break;
}
if ((pointer = pointerItem(pointers, 0))) {
ev.clientX = pointer.clientX;
ev.clientY = pointer.clientY;
}
ev.button =
which < 4
? Math.max(0, which - 1)
: (button & 4 && 1) || button & 2; // left:0 middle:1 right:2
ev.length = pointerLength(pointers);
}
return ev;
}
struct.prototype = {
constructor: struct,
latestTime: 0,
init: function(config) {
var self = this,
handler = (this.handler = function(ev) {
self.handleEvent(ev);
});
this.events = {};
this.duration = isNaN(parseInt(config.duration))
? 600
: parseInt(config.duration);
this.current = parseInt(config.start) || 0;
this.mouse = config.mouse == null ? true : !!config.mouse;
this.interval = parseInt(config.interval) || 5000;
this.playing = config.autoplay == null ? true : !!config.autoplay;
this.arrowkey = !!config.arrowkey;
this.pages = children(this.container);
this.length = this.pages.length;
this.pageData = [];
addListener(
this.container,
STARTEVENT.join(" ") + " click",
handler
);
addListener(
DOC,
MOVEEVENT.join(" ") + (this.arrowkey ? " keydown" : ""),
handler
);
addListener(ROOT, "resize", handler);
each(this.pages, function(page) {
self.pageData.push({
cssText: page.style.cssText || ""
});
});
this.pageData.container = this.container.style.cssText || "";
this.on({
before: function() {
clearTimeout(this.playTimer);
},
dragStart: function() {
clearTimeout(this.playTimer);
removeRange();
},
after: this.firePlay
}).firePlay();
this.resize();
},
resize: function() {
var self = this,
pst = getStyle(this.container, "position"),
css;
this.size = this.getSize(
(this.offsetParent =
this.container[
pst == "absolute" || pst == "fixed"
? "offsetParent"
: "parentNode"
] || DOC.body)
);
css = {
float: "left",
display: "inline"
};
each(this.pages, function(page) {
css["width"] =
self.size -
self.getMarginSize(page) -
self.getPaddingSize(page) -
self.getBorderSize(page) +
"px";
setStyle(page, css);
});
this.total = this.getSum(0, this.length);
css = {};
if (pst == "static") {
css = { position: "relative" };
}
css[transition] = "none";
css["width"] = this.total + "px";
css["left"] = this.getPos(this.current) + "px";
cancelFrame(this.timer);
setStyle(this.container, css);
clearTimeout(this.playTimer);
return this.firePlay();
},
on: function(ev, callback) {
var self = this;
if (type(ev) == "object") {
each(ev, function(ev, callback) {
self.on(ev, callback);
});
} else {
if (!this.events[ev]) {
this.events[ev] = [];
}
this.events[ev].push(callback);
}
return this;
},
fire: function(ev) {
var self = this,
args = slice.call(arguments, 1);
each(this.events[ev] || [], function(func) {
if (isFunction(func)) {
func.apply(self, args);
}
});
return this;
},
isStatic: function() {
return !this.timer && !this.drag;
},
prev: function() {
return this.slide((this.current - 1 + this.length) % this.length);
},
next: function() {
return this.slide((this.current + 1) % this.length);
},
play: function() {
this.playing = true;
return this.firePlay();
},
firePlay: function() {
var self = this;
if (this.playing) {
this.playTimer = setTimeout(function() {
self.next();
}, this.interval);
}
return this;
},
pause: function() {
this.playing = false;
clearTimeout(this.playTimer);
return this;
},
slide: function(_index) {
var self = this,
stime = +new Date(),
duration = this.duration,
current = this.current,
index = Math.min(Math.max(0, _index), this.length - 1),
curSize = this.getSum(index, index + 1),
curPos = parseFloat(getStyle(this.container, "left")) || 0,
css = {},
tarPos;
tarPos = this.getPos(index);
duration *= Math.min(1, Math.abs(tarPos - curPos) / curSize) || 10;
this.current = index;
this.latestTime = stime + duration;
this.fire("before", current, index);
this.end = function() {
delete self.timer;
self.fire("after", index, current);
};
if (transition) {
this.timer = 1;
css[transition] = "left " + duration + "ms ease";
css["left"] = tarPos + "px";
setStyle(this.container, css);
} else {
cancelFrame(this.timer);
ani();
}
function ani() {
var offset = Math.min(duration, +new Date() - stime),
s = EASE(offset, 0, 1, duration);
cp = (tarPos - curPos) * s + curPos;
self.container.style["left"] = cp + "px";
if (offset == duration) {
self.end();
} else {
self.timer = nextFrame(ani);
}
}
},
handleEvent: function(oldEvent) {
var ev = filterEvent(oldEvent),
canDrag =
ev.button < 1 &&
ev.length < 2 &&
(!this.pointerType || this.pointerType == ev.eventType) &&
(this.mouse || ev.pointerType != "mouse");
switch (ev.eventCode) {
case 2:
if (canDrag && this.rect) {
var index = this.current,
rect = [ev.clientX, ev.clientY],
_rect = this.rect,
offset = rect[0] - _rect[0];
if (
this.drag == null &&
_rect.toString() != rect.toString()
) {
this.drag =
Math.abs(offset) >=
Math.abs(rect[1] - _rect[1]);
this.drag && this.fire("dragStart", ev);
}
if (this.drag) {
if (!this.pages[index + (offset > 0 ? -1 : 1)]) {
offset /= Math.abs(offset) / this.size + 2;
}
this.container.style["left"] =
this.startPos + offset + "px";
this.fire("dragMove", ev);
this._offset = offset;
ev.preventDefault();
}
}
break;
case 1:
case 3:
if (canDrag) {
var self = this,
index = this.current,
isDrag,
offset,
tm,
nn,
sub,
curPos,
tarPos,
myWidth;
if (ev.length && (ev.eventCode == 1 || this.drag)) {
nn = ev.target.nodeName.toLowerCase();
clearTimeout(this.eventTimer);
if (!this.pointerType) {
this.pointerType = ev.eventType;
}
this.startPos =
parseFloat(getStyle(this.container, "left")) ||
0;
if (transition) {
this.container.style[transition] = "none";
} else if (this.timer) {
cancelFrame(this.timer);
delete this.timer;
}
this.rect = [ev.clientX, ev.clientY];
this.time = +new Date();
this.container.style["left"] = this.startPos + "px";
if (
ev.eventType != "touch" &&
(nn == "a" || nn == "img")
) {
ev.preventDefault();
}
} else if ((tm = this.time)) {
offset = this._offset || 0;
isDrag = this.drag;
curPos = this.startPos + offset;
tarPos = this.getPos(index);
each(
"rect drag time startPos _offset".split(" "),
function(prop) {
delete self[prop];
}
);
if (isDrag) {
sub = offset > 0 ? 1 : -1;
while (
sub * (curPos - tarPos) >
this.getOuterSize(this.pages[index]) /
2 &&
this.pages[index - sub]
) {
tarPos = this.getPos((index -= sub));
}
if (
Math.abs(offset) > 20 &&
+new Date() - tm < 500
) {
index -= sub;
}
this.fire("dragEnd", ev);
ev.preventDefault();
}
if (curPos != tarPos) {
this.slide(index);
} else if (isDrag) {
this.firePlay();
}
this.eventTimer = setTimeout(function() {
delete self.pointerType;
}, 400);
}
}
break;
case 4:
if (this.timer) {
ev.preventDefault();
}
break;
case 5:
ev.preventDefault();
if (
this.isStatic() &&
+new Date() - this.latestTime >
Math.max(1000 - this.duration, 0)
) {
var wd = ev.wheelDelta || -ev.detail;
Math.abs(wd) >= 3 && this[wd > 0 ? "prev" : "next"]();
}
break;
case 6:
var nn = ev.target.nodeName.toLowerCase();
if (
this.isStatic() &&
nn != "input" &&
nn != "textarea" &&
nn != "select"
) {
switch (ev.keyCode || ev.which) {
case 33:
case 37:
case 38:
this.prev();
break;
case 32:
case 34:
case 39:
case 40:
this.next();
break;
case 35:
this.slide(this.length - 1);
break;
case 36:
this.slide(0);
break;
}
}
break;
case 7:
this.resize();
break;
case 8:
if (oldEvent.propertyName == "left") {
this.container.style[transition] = "none";
this.end();
}
break;
}
},
getSum: function(from, to) {
var sum = 0;
while (from < to) {
sum += this.getOuterSize(this.pages[from++], true);
}
return sum;
},
getPos: function(index) {
var myWidth = this.getOuterSize(this.pages[index], true),
sum =
this.getSum(0, index) +
this["getMarginLeftSize"](this.container) +
this["getBorderLeftSize"](this.container);
return -sum;
},
getOuterSize: function(elem, withMargin) {
return (
elem["offsetWidth"] +
(withMargin ? this.getMarginSize(elem) : 0)
);
},
getInnerSize: function(elem) {
return this.getOuterSize(elem) - this.getBorderSize(elem);
},
getSize: function(elem) {
return (
elem["offsetWidth"] -
this.getPaddingSize(elem) -
this.getBorderSize(elem)
);
},
destroy: function() {
var pageData = this.pageData;
offListener(
this.container,
STARTEVENT.join(" ") + " click",
this.handler
);
offListener(
DOC,
MOVEEVENT.join(" ") + (this.arrowkey ? " keydown" : ""),
this.handler
);
offListener(ROOT, "resize", this.handler);
each(this.pages, function(page, index) {
page.style.cssText = pageData[index].cssText;
});
this.container.style.cssText = pageData.container;
this.container.removeChild(this.comment);
this.length = 0;
return this.pause();
},
refresh: function() {
this.pages = children(this.container);
this.length = this.pages.length;
this.current = Math.max(Math.min(this.length - 1, this.current), 0);
return this.resize();
},
append: function(elem, index) {
if (null == index) {
index = this.pages.length;
}
this.pageData.splice(index, 0, {
cssText: elem.style.cssText || ""
});
this.container.insertBefore(elem, this.pages[index] || null);
return this.refresh();
},
prepend: function(elem) {
return this.append(elem, 0);
},
insertBefore: function(elem, index) {
return this.append(elem, index - 1);
},
insertAfter: function(elem, index) {
return this.append(elem, index + 1);
},
remove: function(index) {
this.container.removeChild(this.pages[index]);
this.pageData.splice(index, 1);
return this.refresh();
}
};
each("margin padding border".split(" "), function(type) {
each("Top Left Right Bottom".split(" "), function(dir) {
var prop = type + dir;
struct.prototype[camelCase("get-" + prop) + "Size"] = function(
elem
) {
return (
parseFloat(
getStyle(elem, prop + (type == "border" ? "Width" : ""))
) || 0
);
};
});
struct.prototype[camelCase("get-" + type) + "Size"] = function(elem) {
return (
this[camelCase("get-" + type) + "Left" + "Size"](elem) +
this[camelCase("get-" + type) + "Right" + "Size"](elem)
);
};
});
if (typeof define == "function" && define.amd) {
define("TouchSlider", function() {
return struct;
});
} else ROOT.TouchSlider = struct;
})(window, function(wrap, config) {
if (!(this instanceof arguments.callee)) {
return new arguments.callee(wrap, config);
}
this.container =
typeof wrap == "string" ? document.getElementById(wrap) : wrap;
this.init(config || {});
});
var thumbnails = document.querySelectorAll("#pagenavi a");
for (var i = 0; i < thumbnails.length; i++) {
var parent = document.getElementById("slider4");
var child = parent.querySelectorAll("li img");
thumbnails[i].style.cssText =
"background: url(" +
child[i].src +
") no-repeat center center; background-size: cover;";
}
var active = 0,
as = document.getElementById("pagenavi").getElementsByTagName("a");
for (var i = 0; i < as.length; i++) {
(function() {
var j = i;
as[i].onclick = function() {
t4.slide(j);
return false;
};
})();
}
var t4 = new TouchSlider("slider4", { speed: 1000, interval: 6000 });
t4.on("before", function(m, n) {
as[m].className = "";
as[n].className = "active";
});
This Pen doesn't use any external JavaScript resources.