-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbleed-slider.js
More file actions
120 lines (102 loc) · 4.3 KB
/
Copy pathbleed-slider.js
File metadata and controls
120 lines (102 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/* =============================================================================
Bleed Slider — OPTIONAL progressive enhancement
-----------------------------------------------------------------------------
The slider works 100% without this file (native scroll-snap). This only adds:
• prev / next arrow buttons (data-bs-prev / data-bs-next)
• click-and-drag scrolling on mouse / trackpad
• auto-disabling arrows at the start / end
Markup it expects (buttons are optional):
<div class="bs-slider">
<div class="bs-container bs-slider__head">
<h2>Featured</h2>
<div class="bs-nav">
<button data-bs-prev aria-label="Previous">‹</button>
<button data-bs-next aria-label="Next">›</button>
</div>
</div>
<ul class="bleed-slider" tabindex="0">…</ul>
</div>
Usage: BleedSlider.init(); // enhances every .bleed-slider
========================================================================== */
(function (global) {
'use strict';
function enhance(track) {
/* opt-out: leave this slider as pure CSS (data-bs-no-enhance) */
if (!track || track.dataset.bsReady || track.hasAttribute('data-bs-no-enhance')) return;
track.dataset.bsReady = '1';
var scope = track.closest('.bs-slider') || track.parentElement;
var prev = scope && scope.querySelector('[data-bs-prev]');
var next = scope && scope.querySelector('[data-bs-next]');
/* width of one "page" jump = first slide + the gap between slides */
function step() {
var first = track.firstElementChild;
if (!first) return track.clientWidth;
var gap = parseFloat(getComputedStyle(track).columnGap) || 0;
return first.getBoundingClientRect().width + gap;
}
function scrollByStep(dir) {
track.scrollBy({ left: dir * step(), behavior: 'smooth' });
}
if (prev) prev.addEventListener('click', function () { scrollByStep(-1); });
if (next) next.addEventListener('click', function () { scrollByStep(1); });
/* enable / disable the arrows at the extremes */
function updateArrows() {
if (!prev && !next) return;
var max = track.scrollWidth - track.clientWidth;
var x = track.scrollLeft;
if (prev) prev.disabled = x <= 1;
if (next) next.disabled = x >= max - 1;
}
track.addEventListener('scroll', updateArrows, { passive: true });
window.addEventListener('resize', updateArrows);
updateArrows();
/* click-and-drag — mouse / trackpad only (touch already scrolls natively) */
if (window.matchMedia && window.matchMedia('(pointer: fine)').matches) {
track.classList.add('is-grab');
var down = false, moved = false, startX = 0, startLeft = 0;
track.addEventListener('pointerdown', function (e) {
if (e.button !== 0) return;
down = true;
moved = false;
startX = e.clientX;
startLeft = track.scrollLeft;
track.setPointerCapture(e.pointerId);
});
track.addEventListener('pointermove', function (e) {
if (!down) return;
var dx = e.clientX - startX;
if (!moved && Math.abs(dx) > 4) {
moved = true;
track.classList.add('is-dragging');
}
if (moved) track.scrollLeft = startLeft - dx;
});
function endDrag(e) {
if (!down) return;
down = false;
track.classList.remove('is-dragging');
if (e && e.pointerId != null) {
try { track.releasePointerCapture(e.pointerId); } catch (_) {}
}
}
track.addEventListener('pointerup', endDrag);
track.addEventListener('pointercancel', endDrag);
/* swallow the click that fires after a real drag */
track.addEventListener('click', function (e) {
if (moved) { e.preventDefault(); e.stopPropagation(); }
}, true);
}
}
function init(root) {
var scope = root || document;
scope.querySelectorAll('.bleed-slider').forEach(enhance);
}
var api = { init: init, enhance: enhance };
if (typeof module !== 'undefined' && module.exports) {
module.exports = api;
} else {
global.BleedSlider = api;
if (document.readyState !== 'loading') init();
else document.addEventListener('DOMContentLoaded', function () { init(); });
}
})(typeof window !== 'undefined' ? window : this);