handy-scroll 1.1.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -93
- package/dist/handy-scroll.d.mts +14 -0
- package/dist/handy-scroll.mjs +124 -0
- package/package.json +16 -25
- package/src/handy-scroll.css +41 -0
- package/src/handy-scroll.mjs +223 -0
- package/dist/handy-scroll.css +0 -1
- package/dist/handy-scroll.d.ts +0 -10
- package/dist/handy-scroll.es6.js +0 -279
- package/dist/handy-scroll.es6.min.js +0 -6
- package/dist/handy-scroll.js +0 -282
- package/dist/handy-scroll.min.js +0 -6
- package/src/dom.js +0 -36
- package/src/handy-scroll-proto.js +0 -153
- package/src/handy-scroll.js +0 -84
- package/src/handy-scroll.less +0 -50
package/dist/handy-scroll.es6.js
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
handy-scroll v1.1.4
|
|
3
|
-
https://amphiluke.github.io/handy-scroll/
|
|
4
|
-
(c) 2023 Amphiluke
|
|
5
|
-
*/
|
|
6
|
-
(function (global, factory) {
|
|
7
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
8
|
-
typeof define === 'function' && define.amd ? define(factory) :
|
|
9
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.handyScroll = factory());
|
|
10
|
-
})(this, (function () { 'use strict';
|
|
11
|
-
|
|
12
|
-
let slice = Array.prototype.slice;
|
|
13
|
-
|
|
14
|
-
let dom = {
|
|
15
|
-
// Precaution to avoid reference errors when imported for SSR (issue #13)
|
|
16
|
-
isDOMAvailable: typeof document === "object" && !!document.documentElement,
|
|
17
|
-
|
|
18
|
-
ready(handler) {
|
|
19
|
-
if (document.readyState === "loading") {
|
|
20
|
-
document.addEventListener("DOMContentLoaded", () => void handler(), {once: true});
|
|
21
|
-
} else {
|
|
22
|
-
handler();
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
$(ref) {
|
|
27
|
-
if (typeof ref === "string") { // ref is a selector
|
|
28
|
-
return document.body.querySelector(ref);
|
|
29
|
-
}
|
|
30
|
-
return ref; // ref is already an element
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
$$(ref) {
|
|
34
|
-
if (Array.isArray(ref)) { // ref is an array of elements
|
|
35
|
-
return ref;
|
|
36
|
-
}
|
|
37
|
-
if (ref.nodeType === Node.ELEMENT_NODE) { // ref is an element
|
|
38
|
-
return [ref];
|
|
39
|
-
}
|
|
40
|
-
if (typeof ref === "string") { // ref is a selector
|
|
41
|
-
return slice.call(document.body.querySelectorAll(ref));
|
|
42
|
-
}
|
|
43
|
-
return slice.call(ref); // ref is an array-like object (NodeList or HTMLCollection)
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
let handyScrollProto = {
|
|
48
|
-
init(container) {
|
|
49
|
-
let instance = this;
|
|
50
|
-
let scrollBodies = dom.$$(".handy-scroll-body")
|
|
51
|
-
.filter(node => node.contains(container));
|
|
52
|
-
if (scrollBodies.length) {
|
|
53
|
-
instance.scrollBody = scrollBodies[0];
|
|
54
|
-
}
|
|
55
|
-
instance.container = container;
|
|
56
|
-
instance.visible = true;
|
|
57
|
-
instance.initWidget();
|
|
58
|
-
instance.update(); // recalculate scrollbar parameters and set its visibility
|
|
59
|
-
instance.addEventHandlers();
|
|
60
|
-
// Set skipSync flags to their initial values (because update() above calls syncWidget())
|
|
61
|
-
instance.skipSyncContainer = instance.skipSyncWidget = false;
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
initWidget() {
|
|
65
|
-
let instance = this;
|
|
66
|
-
let widget = instance.widget = document.createElement("div");
|
|
67
|
-
widget.classList.add("handy-scroll");
|
|
68
|
-
let strut = document.createElement("div");
|
|
69
|
-
strut.style.width = `${instance.container.scrollWidth}px`;
|
|
70
|
-
widget.appendChild(strut);
|
|
71
|
-
instance.container.appendChild(widget);
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
addEventHandlers() {
|
|
75
|
-
let instance = this;
|
|
76
|
-
let eventHandlers = instance.eventHandlers = [
|
|
77
|
-
{
|
|
78
|
-
el: instance.scrollBody || window,
|
|
79
|
-
handlers: {
|
|
80
|
-
scroll() {
|
|
81
|
-
instance.checkVisibility();
|
|
82
|
-
},
|
|
83
|
-
resize() {
|
|
84
|
-
instance.update();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
el: instance.widget,
|
|
90
|
-
handlers: {
|
|
91
|
-
scroll() {
|
|
92
|
-
if (instance.visible && !instance.skipSyncContainer) {
|
|
93
|
-
instance.syncContainer();
|
|
94
|
-
}
|
|
95
|
-
// Resume widget->container syncing after the widget scrolling has finished
|
|
96
|
-
// (it might be temporally disabled by the container while syncing the widget)
|
|
97
|
-
instance.skipSyncContainer = false;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
el: instance.container,
|
|
103
|
-
handlers: {
|
|
104
|
-
scroll() {
|
|
105
|
-
if (!instance.skipSyncWidget) {
|
|
106
|
-
instance.syncWidget();
|
|
107
|
-
}
|
|
108
|
-
// Resume container->widget syncing after the container scrolling has finished
|
|
109
|
-
// (it might be temporally disabled by the widget while syncing the container)
|
|
110
|
-
instance.skipSyncWidget = false;
|
|
111
|
-
},
|
|
112
|
-
focusin() {
|
|
113
|
-
setTimeout(() => {
|
|
114
|
-
// The widget might be destroyed before the timer is triggered (issue #14)
|
|
115
|
-
if (instance.widget) {
|
|
116
|
-
instance.syncWidget();
|
|
117
|
-
}
|
|
118
|
-
}, 0);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
];
|
|
123
|
-
eventHandlers.forEach(({el, handlers}) => {
|
|
124
|
-
Object.keys(handlers).forEach(event => el.addEventListener(event, handlers[event], false));
|
|
125
|
-
});
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
checkVisibility() {
|
|
129
|
-
let instance = this;
|
|
130
|
-
let {widget, container, scrollBody} = instance;
|
|
131
|
-
let mustHide = (widget.scrollWidth <= widget.offsetWidth);
|
|
132
|
-
if (!mustHide) {
|
|
133
|
-
let containerRect = container.getBoundingClientRect();
|
|
134
|
-
let maxVisibleY = scrollBody ?
|
|
135
|
-
scrollBody.getBoundingClientRect().bottom :
|
|
136
|
-
window.innerHeight || document.documentElement.clientHeight;
|
|
137
|
-
mustHide = ((containerRect.bottom <= maxVisibleY) || (containerRect.top > maxVisibleY));
|
|
138
|
-
}
|
|
139
|
-
if (instance.visible === mustHide) {
|
|
140
|
-
instance.visible = !mustHide;
|
|
141
|
-
// We cannot simply hide the scrollbar since its scrollLeft property will not update in that case
|
|
142
|
-
widget.classList.toggle("handy-scroll-hidden");
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
syncContainer() {
|
|
147
|
-
let instance = this;
|
|
148
|
-
let {scrollLeft} = instance.widget;
|
|
149
|
-
if (instance.container.scrollLeft !== scrollLeft) {
|
|
150
|
-
// Prevents container’s “scroll” event handler from syncing back again widget scroll position
|
|
151
|
-
instance.skipSyncWidget = true;
|
|
152
|
-
// Note that this makes container’s “scroll” event handlers execute
|
|
153
|
-
instance.container.scrollLeft = scrollLeft;
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
syncWidget() {
|
|
158
|
-
let instance = this;
|
|
159
|
-
let {scrollLeft} = instance.container;
|
|
160
|
-
if (instance.widget.scrollLeft !== scrollLeft) {
|
|
161
|
-
// Prevents widget’s “scroll” event handler from syncing back again container scroll position
|
|
162
|
-
instance.skipSyncContainer = true;
|
|
163
|
-
// Note that this makes widget’s “scroll” event handlers execute
|
|
164
|
-
instance.widget.scrollLeft = scrollLeft;
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
// Recalculate scroll width and container boundaries
|
|
169
|
-
update() {
|
|
170
|
-
let instance = this;
|
|
171
|
-
let {widget, container, scrollBody} = instance;
|
|
172
|
-
let {clientWidth, scrollWidth} = container;
|
|
173
|
-
widget.style.width = `${clientWidth}px`;
|
|
174
|
-
if (!scrollBody) {
|
|
175
|
-
widget.style.left = `${container.getBoundingClientRect().left}px`;
|
|
176
|
-
}
|
|
177
|
-
widget.firstElementChild.style.width = `${scrollWidth}px`;
|
|
178
|
-
// Fit widget height to the native scroll bar height if needed
|
|
179
|
-
if (scrollWidth > clientWidth) {
|
|
180
|
-
widget.style.height = `${widget.offsetHeight - widget.clientHeight + 1}px`; // +1px JIC
|
|
181
|
-
}
|
|
182
|
-
instance.syncWidget();
|
|
183
|
-
instance.checkVisibility(); // fixes issue Amphiluke/floating-scroll#2
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
// Remove a scrollbar and all related event handlers
|
|
187
|
-
destroy() {
|
|
188
|
-
let instance = this;
|
|
189
|
-
instance.eventHandlers.forEach(({el, handlers}) => {
|
|
190
|
-
Object.keys(handlers).forEach(event => el.removeEventListener(event, handlers[event], false));
|
|
191
|
-
});
|
|
192
|
-
instance.widget.parentNode.removeChild(instance.widget);
|
|
193
|
-
instance.eventHandlers = instance.widget = instance.container = instance.scrollBody = null;
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
let instances = []; // if it were not for IE, it would be better to use Map (container -> instance)
|
|
198
|
-
|
|
199
|
-
let handyScroll = {
|
|
200
|
-
/**
|
|
201
|
-
* Mount widgets in the given containers
|
|
202
|
-
* @param {HTMLElement|NodeList|HTMLCollection|HTMLElement[]|String} containerRef - Widget container reference (either an element, or a list of elements, or a selector)
|
|
203
|
-
*/
|
|
204
|
-
mount(containerRef) {
|
|
205
|
-
dom.$$(containerRef).forEach(container => {
|
|
206
|
-
if (handyScroll.mounted(container)) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
let instance = Object.create(handyScrollProto);
|
|
210
|
-
instances.push(instance);
|
|
211
|
-
instance.init(container);
|
|
212
|
-
});
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Check if a widget is already mounted in the given container
|
|
217
|
-
* @param {HTMLElement|String} containerRef - Widget container reference (either an element, or a selector)
|
|
218
|
-
* @returns {Boolean}
|
|
219
|
-
*/
|
|
220
|
-
mounted(containerRef) {
|
|
221
|
-
let container = dom.$(containerRef);
|
|
222
|
-
return instances.some(instance => instance.container === container);
|
|
223
|
-
},
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Update widget parameters and position
|
|
227
|
-
* @param {HTMLElement|NodeList|HTMLCollection|HTMLElement[]|String} containerRef - Widget container reference (either an element, or a list of elements, or a selector)
|
|
228
|
-
*/
|
|
229
|
-
update(containerRef) {
|
|
230
|
-
dom.$$(containerRef).forEach(container => {
|
|
231
|
-
instances.some(instance => {
|
|
232
|
-
if (instance.container === container) {
|
|
233
|
-
instance.update();
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
return false;
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Destroy widgets mounted in the given containers
|
|
243
|
-
* @param {HTMLElement|NodeList|HTMLCollection|HTMLElement[]|String} containerRef - Widget container reference (either an element, or a list of elements, or a selector)
|
|
244
|
-
*/
|
|
245
|
-
destroy(containerRef) {
|
|
246
|
-
dom.$$(containerRef).forEach(container => {
|
|
247
|
-
instances.some((instance, index) => {
|
|
248
|
-
if (instance.container === container) {
|
|
249
|
-
instances.splice(index, 1)[0].destroy();
|
|
250
|
-
return true;
|
|
251
|
-
}
|
|
252
|
-
return false;
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
},
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Destroy handyScroll widgets whose containers are not in the document anymore
|
|
259
|
-
*/
|
|
260
|
-
destroyDetached() {
|
|
261
|
-
instances = instances.filter(instance => {
|
|
262
|
-
if (!document.body.contains(instance.container)) {
|
|
263
|
-
instance.destroy();
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
return true;
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
if (dom.isDOMAvailable) {
|
|
272
|
-
dom.ready(() => {
|
|
273
|
-
handyScroll.mount("[data-handy-scroll]");
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return handyScroll;
|
|
278
|
-
|
|
279
|
-
}));
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
handy-scroll v1.1.4
|
|
3
|
-
https://amphiluke.github.io/handy-scroll/
|
|
4
|
-
(c) 2023 Amphiluke
|
|
5
|
-
*/
|
|
6
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).handyScroll=t()}(this,(function(){"use strict";let e=Array.prototype.slice,t={isDOMAvailable:"object"==typeof document&&!!document.documentElement,ready(e){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>{e()}),{once:!0}):e()},$:e=>"string"==typeof e?document.body.querySelector(e):e,$$:t=>Array.isArray(t)?t:t.nodeType===Node.ELEMENT_NODE?[t]:"string"==typeof t?e.call(document.body.querySelectorAll(t)):e.call(t)},n={init(e){let n=this,i=t.$$(".handy-scroll-body").filter((t=>t.contains(e)));i.length&&(n.scrollBody=i[0]),n.container=e,n.visible=!0,n.initWidget(),n.update(),n.addEventHandlers(),n.skipSyncContainer=n.skipSyncWidget=!1},initWidget(){let e=this,t=e.widget=document.createElement("div");t.classList.add("handy-scroll");let n=document.createElement("div");n.style.width=`${e.container.scrollWidth}px`,t.appendChild(n),e.container.appendChild(t)},addEventHandlers(){let e=this;(e.eventHandlers=[{el:e.scrollBody||window,handlers:{scroll(){e.checkVisibility()},resize(){e.update()}}},{el:e.widget,handlers:{scroll(){e.visible&&!e.skipSyncContainer&&e.syncContainer(),e.skipSyncContainer=!1}}},{el:e.container,handlers:{scroll(){e.skipSyncWidget||e.syncWidget(),e.skipSyncWidget=!1},focusin(){setTimeout((()=>{e.widget&&e.syncWidget()}),0)}}}]).forEach((({el:e,handlers:t})=>{Object.keys(t).forEach((n=>e.addEventListener(n,t[n],!1)))}))},checkVisibility(){let e=this,{widget:t,container:n,scrollBody:i}=e,l=t.scrollWidth<=t.offsetWidth;if(!l){let e=n.getBoundingClientRect(),t=i?i.getBoundingClientRect().bottom:window.innerHeight||document.documentElement.clientHeight;l=e.bottom<=t||e.top>t}e.visible===l&&(e.visible=!l,t.classList.toggle("handy-scroll-hidden"))},syncContainer(){let e=this,{scrollLeft:t}=e.widget;e.container.scrollLeft!==t&&(e.skipSyncWidget=!0,e.container.scrollLeft=t)},syncWidget(){let e=this,{scrollLeft:t}=e.container;e.widget.scrollLeft!==t&&(e.skipSyncContainer=!0,e.widget.scrollLeft=t)},update(){let e=this,{widget:t,container:n,scrollBody:i}=e,{clientWidth:l,scrollWidth:o}=n;t.style.width=`${l}px`,i||(t.style.left=`${n.getBoundingClientRect().left}px`),t.firstElementChild.style.width=`${o}px`,o>l&&(t.style.height=t.offsetHeight-t.clientHeight+1+"px"),e.syncWidget(),e.checkVisibility()},destroy(){let e=this;e.eventHandlers.forEach((({el:e,handlers:t})=>{Object.keys(t).forEach((n=>e.removeEventListener(n,t[n],!1)))})),e.widget.parentNode.removeChild(e.widget),e.eventHandlers=e.widget=e.container=e.scrollBody=null}},i=[],l={mount(e){t.$$(e).forEach((e=>{if(l.mounted(e))return;let t=Object.create(n);i.push(t),t.init(e)}))},mounted(e){let n=t.$(e);return i.some((e=>e.container===n))},update(e){t.$$(e).forEach((e=>{i.some((t=>t.container===e&&(t.update(),!0)))}))},destroy(e){t.$$(e).forEach((e=>{i.some(((t,n)=>t.container===e&&(i.splice(n,1)[0].destroy(),!0)))}))},destroyDetached(){i=i.filter((e=>!!document.body.contains(e.container)||(e.destroy(),!1)))}};return t.isDOMAvailable&&t.ready((()=>{l.mount("[data-handy-scroll]")})),l}));
|
package/dist/handy-scroll.js
DELETED
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
handy-scroll v1.1.4
|
|
3
|
-
https://amphiluke.github.io/handy-scroll/
|
|
4
|
-
(c) 2023 Amphiluke
|
|
5
|
-
*/
|
|
6
|
-
(function (global, factory) {
|
|
7
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
8
|
-
typeof define === 'function' && define.amd ? define(factory) :
|
|
9
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.handyScroll = factory());
|
|
10
|
-
})(this, (function () { 'use strict';
|
|
11
|
-
|
|
12
|
-
var slice = Array.prototype.slice;
|
|
13
|
-
var dom = {
|
|
14
|
-
// Precaution to avoid reference errors when imported for SSR (issue #13)
|
|
15
|
-
isDOMAvailable: typeof document === "object" && !!document.documentElement,
|
|
16
|
-
ready: function ready(handler) {
|
|
17
|
-
if (document.readyState === "loading") {
|
|
18
|
-
document.addEventListener("DOMContentLoaded", function () {
|
|
19
|
-
return void handler();
|
|
20
|
-
}, {
|
|
21
|
-
once: true
|
|
22
|
-
});
|
|
23
|
-
} else {
|
|
24
|
-
handler();
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
$: function $(ref) {
|
|
28
|
-
if (typeof ref === "string") {
|
|
29
|
-
// ref is a selector
|
|
30
|
-
return document.body.querySelector(ref);
|
|
31
|
-
}
|
|
32
|
-
return ref; // ref is already an element
|
|
33
|
-
},
|
|
34
|
-
$$: function $$(ref) {
|
|
35
|
-
if (Array.isArray(ref)) {
|
|
36
|
-
// ref is an array of elements
|
|
37
|
-
return ref;
|
|
38
|
-
}
|
|
39
|
-
if (ref.nodeType === Node.ELEMENT_NODE) {
|
|
40
|
-
// ref is an element
|
|
41
|
-
return [ref];
|
|
42
|
-
}
|
|
43
|
-
if (typeof ref === "string") {
|
|
44
|
-
// ref is a selector
|
|
45
|
-
return slice.call(document.body.querySelectorAll(ref));
|
|
46
|
-
}
|
|
47
|
-
return slice.call(ref); // ref is an array-like object (NodeList or HTMLCollection)
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
var handyScrollProto = {
|
|
52
|
-
init: function init(container) {
|
|
53
|
-
var instance = this;
|
|
54
|
-
var scrollBodies = dom.$$(".handy-scroll-body").filter(function (node) {
|
|
55
|
-
return node.contains(container);
|
|
56
|
-
});
|
|
57
|
-
if (scrollBodies.length) {
|
|
58
|
-
instance.scrollBody = scrollBodies[0];
|
|
59
|
-
}
|
|
60
|
-
instance.container = container;
|
|
61
|
-
instance.visible = true;
|
|
62
|
-
instance.initWidget();
|
|
63
|
-
instance.update(); // recalculate scrollbar parameters and set its visibility
|
|
64
|
-
instance.addEventHandlers();
|
|
65
|
-
// Set skipSync flags to their initial values (because update() above calls syncWidget())
|
|
66
|
-
instance.skipSyncContainer = instance.skipSyncWidget = false;
|
|
67
|
-
},
|
|
68
|
-
initWidget: function initWidget() {
|
|
69
|
-
var instance = this;
|
|
70
|
-
var widget = instance.widget = document.createElement("div");
|
|
71
|
-
widget.classList.add("handy-scroll");
|
|
72
|
-
var strut = document.createElement("div");
|
|
73
|
-
strut.style.width = instance.container.scrollWidth + "px";
|
|
74
|
-
widget.appendChild(strut);
|
|
75
|
-
instance.container.appendChild(widget);
|
|
76
|
-
},
|
|
77
|
-
addEventHandlers: function addEventHandlers() {
|
|
78
|
-
var instance = this;
|
|
79
|
-
var eventHandlers = instance.eventHandlers = [{
|
|
80
|
-
el: instance.scrollBody || window,
|
|
81
|
-
handlers: {
|
|
82
|
-
scroll: function scroll() {
|
|
83
|
-
instance.checkVisibility();
|
|
84
|
-
},
|
|
85
|
-
resize: function resize() {
|
|
86
|
-
instance.update();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}, {
|
|
90
|
-
el: instance.widget,
|
|
91
|
-
handlers: {
|
|
92
|
-
scroll: function scroll() {
|
|
93
|
-
if (instance.visible && !instance.skipSyncContainer) {
|
|
94
|
-
instance.syncContainer();
|
|
95
|
-
}
|
|
96
|
-
// Resume widget->container syncing after the widget scrolling has finished
|
|
97
|
-
// (it might be temporally disabled by the container while syncing the widget)
|
|
98
|
-
instance.skipSyncContainer = false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}, {
|
|
102
|
-
el: instance.container,
|
|
103
|
-
handlers: {
|
|
104
|
-
scroll: function scroll() {
|
|
105
|
-
if (!instance.skipSyncWidget) {
|
|
106
|
-
instance.syncWidget();
|
|
107
|
-
}
|
|
108
|
-
// Resume container->widget syncing after the container scrolling has finished
|
|
109
|
-
// (it might be temporally disabled by the widget while syncing the container)
|
|
110
|
-
instance.skipSyncWidget = false;
|
|
111
|
-
},
|
|
112
|
-
focusin: function focusin() {
|
|
113
|
-
setTimeout(function () {
|
|
114
|
-
// The widget might be destroyed before the timer is triggered (issue #14)
|
|
115
|
-
if (instance.widget) {
|
|
116
|
-
instance.syncWidget();
|
|
117
|
-
}
|
|
118
|
-
}, 0);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}];
|
|
122
|
-
eventHandlers.forEach(function (_ref) {
|
|
123
|
-
var el = _ref.el,
|
|
124
|
-
handlers = _ref.handlers;
|
|
125
|
-
Object.keys(handlers).forEach(function (event) {
|
|
126
|
-
return el.addEventListener(event, handlers[event], false);
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
},
|
|
130
|
-
checkVisibility: function checkVisibility() {
|
|
131
|
-
var instance = this;
|
|
132
|
-
var widget = instance.widget,
|
|
133
|
-
container = instance.container,
|
|
134
|
-
scrollBody = instance.scrollBody;
|
|
135
|
-
var mustHide = widget.scrollWidth <= widget.offsetWidth;
|
|
136
|
-
if (!mustHide) {
|
|
137
|
-
var containerRect = container.getBoundingClientRect();
|
|
138
|
-
var maxVisibleY = scrollBody ? scrollBody.getBoundingClientRect().bottom : window.innerHeight || document.documentElement.clientHeight;
|
|
139
|
-
mustHide = containerRect.bottom <= maxVisibleY || containerRect.top > maxVisibleY;
|
|
140
|
-
}
|
|
141
|
-
if (instance.visible === mustHide) {
|
|
142
|
-
instance.visible = !mustHide;
|
|
143
|
-
// We cannot simply hide the scrollbar since its scrollLeft property will not update in that case
|
|
144
|
-
widget.classList.toggle("handy-scroll-hidden");
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
syncContainer: function syncContainer() {
|
|
148
|
-
var instance = this;
|
|
149
|
-
var scrollLeft = instance.widget.scrollLeft;
|
|
150
|
-
if (instance.container.scrollLeft !== scrollLeft) {
|
|
151
|
-
// Prevents container’s “scroll” event handler from syncing back again widget scroll position
|
|
152
|
-
instance.skipSyncWidget = true;
|
|
153
|
-
// Note that this makes container’s “scroll” event handlers execute
|
|
154
|
-
instance.container.scrollLeft = scrollLeft;
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
syncWidget: function syncWidget() {
|
|
158
|
-
var instance = this;
|
|
159
|
-
var scrollLeft = instance.container.scrollLeft;
|
|
160
|
-
if (instance.widget.scrollLeft !== scrollLeft) {
|
|
161
|
-
// Prevents widget’s “scroll” event handler from syncing back again container scroll position
|
|
162
|
-
instance.skipSyncContainer = true;
|
|
163
|
-
// Note that this makes widget’s “scroll” event handlers execute
|
|
164
|
-
instance.widget.scrollLeft = scrollLeft;
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
// Recalculate scroll width and container boundaries
|
|
168
|
-
update: function update() {
|
|
169
|
-
var instance = this;
|
|
170
|
-
var widget = instance.widget,
|
|
171
|
-
container = instance.container,
|
|
172
|
-
scrollBody = instance.scrollBody;
|
|
173
|
-
var clientWidth = container.clientWidth,
|
|
174
|
-
scrollWidth = container.scrollWidth;
|
|
175
|
-
widget.style.width = clientWidth + "px";
|
|
176
|
-
if (!scrollBody) {
|
|
177
|
-
widget.style.left = container.getBoundingClientRect().left + "px";
|
|
178
|
-
}
|
|
179
|
-
widget.firstElementChild.style.width = scrollWidth + "px";
|
|
180
|
-
// Fit widget height to the native scroll bar height if needed
|
|
181
|
-
if (scrollWidth > clientWidth) {
|
|
182
|
-
widget.style.height = widget.offsetHeight - widget.clientHeight + 1 + "px"; // +1px JIC
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
instance.syncWidget();
|
|
186
|
-
instance.checkVisibility(); // fixes issue Amphiluke/floating-scroll#2
|
|
187
|
-
},
|
|
188
|
-
// Remove a scrollbar and all related event handlers
|
|
189
|
-
destroy: function destroy() {
|
|
190
|
-
var instance = this;
|
|
191
|
-
instance.eventHandlers.forEach(function (_ref2) {
|
|
192
|
-
var el = _ref2.el,
|
|
193
|
-
handlers = _ref2.handlers;
|
|
194
|
-
Object.keys(handlers).forEach(function (event) {
|
|
195
|
-
return el.removeEventListener(event, handlers[event], false);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
instance.widget.parentNode.removeChild(instance.widget);
|
|
199
|
-
instance.eventHandlers = instance.widget = instance.container = instance.scrollBody = null;
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
var instances = []; // if it were not for IE, it would be better to use Map (container -> instance)
|
|
204
|
-
|
|
205
|
-
var handyScroll = {
|
|
206
|
-
/**
|
|
207
|
-
* Mount widgets in the given containers
|
|
208
|
-
* @param {HTMLElement|NodeList|HTMLCollection|HTMLElement[]|String} containerRef - Widget container reference (either an element, or a list of elements, or a selector)
|
|
209
|
-
*/
|
|
210
|
-
mount: function mount(containerRef) {
|
|
211
|
-
dom.$$(containerRef).forEach(function (container) {
|
|
212
|
-
if (handyScroll.mounted(container)) {
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
var instance = Object.create(handyScrollProto);
|
|
216
|
-
instances.push(instance);
|
|
217
|
-
instance.init(container);
|
|
218
|
-
});
|
|
219
|
-
},
|
|
220
|
-
/**
|
|
221
|
-
* Check if a widget is already mounted in the given container
|
|
222
|
-
* @param {HTMLElement|String} containerRef - Widget container reference (either an element, or a selector)
|
|
223
|
-
* @returns {Boolean}
|
|
224
|
-
*/
|
|
225
|
-
mounted: function mounted(containerRef) {
|
|
226
|
-
var container = dom.$(containerRef);
|
|
227
|
-
return instances.some(function (instance) {
|
|
228
|
-
return instance.container === container;
|
|
229
|
-
});
|
|
230
|
-
},
|
|
231
|
-
/**
|
|
232
|
-
* Update widget parameters and position
|
|
233
|
-
* @param {HTMLElement|NodeList|HTMLCollection|HTMLElement[]|String} containerRef - Widget container reference (either an element, or a list of elements, or a selector)
|
|
234
|
-
*/
|
|
235
|
-
update: function update(containerRef) {
|
|
236
|
-
dom.$$(containerRef).forEach(function (container) {
|
|
237
|
-
instances.some(function (instance) {
|
|
238
|
-
if (instance.container === container) {
|
|
239
|
-
instance.update();
|
|
240
|
-
return true;
|
|
241
|
-
}
|
|
242
|
-
return false;
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
},
|
|
246
|
-
/**
|
|
247
|
-
* Destroy widgets mounted in the given containers
|
|
248
|
-
* @param {HTMLElement|NodeList|HTMLCollection|HTMLElement[]|String} containerRef - Widget container reference (either an element, or a list of elements, or a selector)
|
|
249
|
-
*/
|
|
250
|
-
destroy: function destroy(containerRef) {
|
|
251
|
-
dom.$$(containerRef).forEach(function (container) {
|
|
252
|
-
instances.some(function (instance, index) {
|
|
253
|
-
if (instance.container === container) {
|
|
254
|
-
instances.splice(index, 1)[0].destroy();
|
|
255
|
-
return true;
|
|
256
|
-
}
|
|
257
|
-
return false;
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
},
|
|
261
|
-
/**
|
|
262
|
-
* Destroy handyScroll widgets whose containers are not in the document anymore
|
|
263
|
-
*/
|
|
264
|
-
destroyDetached: function destroyDetached() {
|
|
265
|
-
instances = instances.filter(function (instance) {
|
|
266
|
-
if (!document.body.contains(instance.container)) {
|
|
267
|
-
instance.destroy();
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
return true;
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
if (dom.isDOMAvailable) {
|
|
275
|
-
dom.ready(function () {
|
|
276
|
-
handyScroll.mount("[data-handy-scroll]");
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return handyScroll;
|
|
281
|
-
|
|
282
|
-
}));
|
package/dist/handy-scroll.min.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
handy-scroll v1.1.4
|
|
3
|
-
https://amphiluke.github.io/handy-scroll/
|
|
4
|
-
(c) 2023 Amphiluke
|
|
5
|
-
*/
|
|
6
|
-
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(t="undefined"!=typeof globalThis?globalThis:t||self).handyScroll=n()}(this,(function(){"use strict";var t=Array.prototype.slice,n="object"==typeof document&&!!document.documentElement,e=function(t){return"string"==typeof t?document.body.querySelector(t):t},i=function(n){return Array.isArray(n)?n:n.nodeType===Node.ELEMENT_NODE?[n]:"string"==typeof n?t.call(document.body.querySelectorAll(n)):t.call(n)},o={init:function(t){var n=this,e=i(".handy-scroll-body").filter((function(n){return n.contains(t)}));e.length&&(n.scrollBody=e[0]),n.container=t,n.visible=!0,n.initWidget(),n.update(),n.addEventHandlers(),n.skipSyncContainer=n.skipSyncWidget=!1},initWidget:function(){var t=this,n=t.widget=document.createElement("div");n.classList.add("handy-scroll");var e=document.createElement("div");e.style.width=t.container.scrollWidth+"px",n.appendChild(e),t.container.appendChild(n)},addEventHandlers:function(){var t=this;(t.eventHandlers=[{el:t.scrollBody||window,handlers:{scroll:function(){t.checkVisibility()},resize:function(){t.update()}}},{el:t.widget,handlers:{scroll:function(){t.visible&&!t.skipSyncContainer&&t.syncContainer(),t.skipSyncContainer=!1}}},{el:t.container,handlers:{scroll:function(){t.skipSyncWidget||t.syncWidget(),t.skipSyncWidget=!1},focusin:function(){setTimeout((function(){t.widget&&t.syncWidget()}),0)}}}]).forEach((function(t){var n=t.el,e=t.handlers;Object.keys(e).forEach((function(t){return n.addEventListener(t,e[t],!1)}))}))},checkVisibility:function(){var t=this,n=t.widget,e=t.container,i=t.scrollBody,o=n.scrollWidth<=n.offsetWidth;if(!o){var c=e.getBoundingClientRect(),r=i?i.getBoundingClientRect().bottom:window.innerHeight||document.documentElement.clientHeight;o=c.bottom<=r||c.top>r}t.visible===o&&(t.visible=!o,n.classList.toggle("handy-scroll-hidden"))},syncContainer:function(){var t=this,n=t.widget.scrollLeft;t.container.scrollLeft!==n&&(t.skipSyncWidget=!0,t.container.scrollLeft=n)},syncWidget:function(){var t=this,n=t.container.scrollLeft;t.widget.scrollLeft!==n&&(t.skipSyncContainer=!0,t.widget.scrollLeft=n)},update:function(){var t=this,n=t.widget,e=t.container,i=t.scrollBody,o=e.clientWidth,c=e.scrollWidth;n.style.width=o+"px",i||(n.style.left=e.getBoundingClientRect().left+"px"),n.firstElementChild.style.width=c+"px",c>o&&(n.style.height=n.offsetHeight-n.clientHeight+1+"px"),t.syncWidget(),t.checkVisibility()},destroy:function(){var t=this;t.eventHandlers.forEach((function(t){var n=t.el,e=t.handlers;Object.keys(e).forEach((function(t){return n.removeEventListener(t,e[t],!1)}))})),t.widget.parentNode.removeChild(t.widget),t.eventHandlers=t.widget=t.container=t.scrollBody=null}},c=[],r={mount:function(t){i(t).forEach((function(t){if(!r.mounted(t)){var n=Object.create(o);c.push(n),n.init(t)}}))},mounted:function(t){var n=e(t);return c.some((function(t){return t.container===n}))},update:function(t){i(t).forEach((function(t){c.some((function(n){return n.container===t&&(n.update(),!0)}))}))},destroy:function(t){i(t).forEach((function(t){c.some((function(n,e){return n.container===t&&(c.splice(e,1)[0].destroy(),!0)}))}))},destroyDetached:function(){c=c.filter((function(t){return!!document.body.contains(t.container)||(t.destroy(),!1)}))}};return n&&function(t){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(function(){t()}),{once:!0}):t()}((function(){r.mount("[data-handy-scroll]")})),r}));
|
package/src/dom.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
let slice = Array.prototype.slice;
|
|
2
|
-
|
|
3
|
-
let dom = {
|
|
4
|
-
// Precaution to avoid reference errors when imported for SSR (issue #13)
|
|
5
|
-
isDOMAvailable: typeof document === "object" && !!document.documentElement,
|
|
6
|
-
|
|
7
|
-
ready(handler) {
|
|
8
|
-
if (document.readyState === "loading") {
|
|
9
|
-
document.addEventListener("DOMContentLoaded", () => void handler(), {once: true});
|
|
10
|
-
} else {
|
|
11
|
-
handler();
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
$(ref) {
|
|
16
|
-
if (typeof ref === "string") { // ref is a selector
|
|
17
|
-
return document.body.querySelector(ref);
|
|
18
|
-
}
|
|
19
|
-
return ref; // ref is already an element
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
$$(ref) {
|
|
23
|
-
if (Array.isArray(ref)) { // ref is an array of elements
|
|
24
|
-
return ref;
|
|
25
|
-
}
|
|
26
|
-
if (ref.nodeType === Node.ELEMENT_NODE) { // ref is an element
|
|
27
|
-
return [ref];
|
|
28
|
-
}
|
|
29
|
-
if (typeof ref === "string") { // ref is a selector
|
|
30
|
-
return slice.call(document.body.querySelectorAll(ref));
|
|
31
|
-
}
|
|
32
|
-
return slice.call(ref); // ref is an array-like object (NodeList or HTMLCollection)
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export default dom;
|