os-scroll-chain
Version:
OverlayScrollbars Extension to prevent scroll chaining
180 lines (160 loc) • 7.37 kB
JavaScript
/**
* os-scroll-chain v2.0.2
* (c) 2022 Hamed Ehtesham
* Released under the MIT License.
*/
(function () {
'use strict';
OverlayScrollbars.extension('scroll-chain', function (defaultOptions, framework, compatibility) {
defaultOptions = defaultOptions || {};
var osInstance = this;
var instanceElements = osInstance.getElements();
var $viewport = framework(instanceElements.viewport);
var preventVertical = defaultOptions.vertical || true;
var preventHorizontal = defaultOptions.horizontal || true;
/*
* each event should be processed by the nearest parent scrollbar
*/
function shouldProcess(event) {
var foundViewport = false;
var self = event.target;
while (!foundViewport) {
if (!self) {
return false;
} else if (self.className && self.className.startsWith('os-viewport')) {
foundViewport = true;
if (self !== instanceElements.viewport) return false;
}
self = self.parentNode;
}
return true;
}
var trapWheel = function trapWheel(event) {
if (!shouldProcess(event)) return;
var isHorizontal = event.shiftKey || false;
if (isHorizontal) {
if (preventHorizontal) {
// cross-browser wheel delta
event = window.event || event;
var delta = Math.min(1, Math.max(-1, -event.wheelDelta || event.deltaY || event.detail));
var xScrollPos = $viewport.scrollLeft() + 0.5;
// only trap events once we've scrolled to the end or beginning
if (delta > 0 && xScrollPos >= osInstance.scroll().max.x || delta < 0 && xScrollPos <= 1) {
compatibility.prvD(event);
return false;
}
}
} else if (preventVertical) {
// cross-browser wheel delta
event = window.event || event;
var _delta = Math.min(1, Math.max(-1, -event.wheelDelta || event.deltaY || event.detail));
var yScrollPos = $viewport.scrollTop() + 0.5;
// only trap events once we've scrolled to the end or beginning
if (_delta > 0 && yScrollPos >= osInstance.scroll().max.y || _delta < 0 && yScrollPos <= 1) {
compatibility.prvD(event);
return false;
}
}
};
var xStart = void 0,
yStart = void 0,
firstTime = void 0,
parentScroll = void 0;
var handleTouchStart = function handleTouchStart(event) {
xStart = event.touches[0].clientX;
yStart = event.touches[0].clientY;
firstTime = true;
parentScroll = false;
};
/*
* detect significant axis between x, y
*/
function significant(x, y) {
var xAbs = Math.abs(x);
var yAbs = Math.abs(y);
var xSignificant = true;
var ySignificant = true;
if (xAbs - yAbs > 0) {
if (xAbs / yAbs >= 2) ySignificant = false;
} else {
if (yAbs / xAbs >= 2) xSignificant = false;
}
return { ySignificant: ySignificant, xSignificant: xSignificant };
}
var handleTouchMove = function handleTouchMove(event) {
if (!shouldProcess(event)) return;
if (parentScroll) {
compatibility.prvD(event);
return false;
} else if (!firstTime) return;
if (!yStart || !xStart) return;
var yEnd = event.touches[0].clientY;
var xEnd = event.touches[0].clientX;
var yDiff = yStart - yEnd;
var xDiff = xStart - xEnd;
// detect significant axis
var _significant = significant(xDiff, yDiff),
ySignificant = _significant.ySignificant,
xSignificant = _significant.xSignificant;
if (preventVertical && ySignificant) {
var yScrollPos = $viewport.scrollTop() + 0.5;
if (yDiff > 0 && yScrollPos >= osInstance.scroll().max.y || yDiff < 0 && yScrollPos <= 1) {
if (firstTime) {
parentScroll = true;
}
compatibility.prvD(event);
}
}
if (preventHorizontal && xSignificant) {
var xScrollPos = $viewport.scrollLeft() + 0.5;
if (xDiff > 0 && xScrollPos >= osInstance.scroll().max.x || xDiff < 0 && xScrollPos <= 1) {
if (firstTime) {
parentScroll = true;
}
compatibility.prvD(event);
}
}
firstTime = false;
};
var isEventSupported = function isEventSupported(eventName) {
var TAGNAMES = {
'select': 'input', 'change': 'input',
'submit': 'form', 'reset': 'form',
'error': 'img', 'load': 'img', 'abort': 'img'
};
var el = document.createElement(TAGNAMES[eventName] || 'div');
eventName = 'on' + eventName;
var isSupported = eventName in el;
if (!isSupported) {
el.setAttribute(eventName, 'return;');
isSupported = typeof el[eventName] == 'function';
}
el = null;
return isSupported;
};
return {
added: function added(options) {
options = options || {};
preventVertical = options.vertical !== undefined ? !!options.vertical : preventVertical;
preventHorizontal = options.horizontal !== undefined ? !!options.horizontal : preventHorizontal;
if (isEventSupported('wheel')) $viewport.on('wheel', trapWheel);else $viewport.on('mousewheel DOMMouseScroll', trapWheel);
$viewport.on('touchstart', handleTouchStart);
$viewport.on('touchmove', handleTouchMove);
if (preventVertical && preventHorizontal) $viewport.css('overscroll-behavior', 'none');else {
if (preventVertical) $viewport.css('overscroll-behavior-y', 'none');
if (preventHorizontal) $viewport.css('overscroll-behavior-x', 'none');
}
},
removed: function removed() {
if (isEventSupported('wheel')) $viewport.off('wheel', trapWheel);else $viewport.off('mousewheel DOMMouseScroll', trapWheel);
$viewport.off('touchstart', handleTouchStart);
$viewport.off('touchmove', handleTouchMove);
if (preventVertical && preventHorizontal) $viewport.css('overscroll-behavior', 'unset');else {
if (preventVertical) $viewport.css('overscroll-behavior-y', 'unset');
if (preventHorizontal) $viewport.css('overscroll-behavior-x', 'unset');
}
}
};
});
})();
//# sourceMappingURL=os-scroll-chain.js.map