KOKINIO - MANAGER
Edit File: bodyScrollLock.js
(function (global, factory) { if (typeof define === "function" && define.amd) { define(['exports'], factory); } else if (typeof exports !== "undefined") { factory(exports); } else { var mod = { exports: {} }; factory(mod.exports); global.bodyScrollLock = mod.exports; } })(this, function (exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } // Older browsers don't support event options, feature detect it. // Adopted and modified solution from Bohdan Didukh (2017) // https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi var hasPassiveEvents = false; if (typeof window !== 'undefined') { var passiveTestOptions = { get passive() { hasPassiveEvents = true; return undefined; } }; window.addEventListener('testPassive', null, passiveTestOptions); window.removeEventListener('testPassive', null, passiveTestOptions); } var isIosDevice = typeof window !== 'undefined' && window.navigator && window.navigator.platform && /iP(ad|hone|od)/.test(window.navigator.platform); var locks = []; var documentListenerAdded = false; var initialClientY = -1; var previousBodyOverflowSetting = void 0; var previousBodyPaddingRight = void 0; // returns true if `el` should be allowed to receive touchmove events var allowTouchMove = function allowTouchMove(el) { return locks.some(function (lock) { if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) { return true; } return false; }); }; var preventDefault = function preventDefault(rawEvent) { var e = rawEvent || window.event; // For the case whereby consumers adds a touchmove event listener to document. // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false }) // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then // the touchmove event on document will break. if (allowTouchMove(e.target)) { return true; } // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom) if (e.touches.length > 1) return true; if (e.preventDefault) e.preventDefault(); return false; }; var setOverflowHidden = function setOverflowHidden(options) { // Setting overflow on body/documentElement synchronously in Desktop Safari slows down // the responsiveness for some reason. Setting within a setTimeout fixes this. setTimeout(function () { // If previousBodyPaddingRight is already set, don't set it again. if (previousBodyPaddingRight === undefined) { var _reserveScrollBarGap = !!options && options.reserveScrollBarGap === true; var scrollBarGap = window.innerWidth - document.documentElement.clientWidth; if (_reserveScrollBarGap && scrollBarGap > 0) { previousBodyPaddingRight = document.body.style.paddingRight; document.body.style.paddingRight = scrollBarGap + 'px'; } } // If previousBodyOverflowSetting is already set, don't set it again. if (previousBodyOverflowSetting === undefined) { previousBodyOverflowSetting = document.body.style.overflow; document.body.style.overflow = 'hidden'; } }); }; var restoreOverflowSetting = function restoreOverflowSetting() { // Setting overflow on body/documentElement synchronously in Desktop Safari slows down // the responsiveness for some reason. Setting within a setTimeout fixes this. setTimeout(function () { if (previousBodyPaddingRight !== undefined) { document.body.style.paddingRight = previousBodyPaddingRight; // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it // can be set again. previousBodyPaddingRight = undefined; } if (previousBodyOverflowSetting !== undefined) { document.body.style.overflow = previousBodyOverflowSetting; // Restore previousBodyOverflowSetting to undefined // so setOverflowHidden knows it can be set again. previousBodyOverflowSetting = undefined; } }); }; // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions var isTargetElementTotallyScrolled = function isTargetElementTotallyScrolled(targetElement) { return targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false; }; var handleScroll = function handleScroll(event, targetElement) { var clientY = event.targetTouches[0].clientY - initialClientY; if (allowTouchMove(event.target)) { return false; } if (targetElement && targetElement.scrollTop === 0 && clientY > 0) { // element is at the top of its scroll return preventDefault(event); } if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) { // element is at the top of its scroll return preventDefault(event); } event.stopPropagation(); return true; }; var disableBodyScroll = exports.disableBodyScroll = function disableBodyScroll(targetElement, options) { if (isIosDevice) { // targetElement must be provided, and disableBodyScroll must not have been // called on this targetElement before. if (!targetElement) { // eslint-disable-next-line no-console console.error('disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.'); return; } if (targetElement && !locks.some(function (lock) { return lock.targetElement === targetElement; })) { var lock = { targetElement: targetElement, options: options || {} }; locks = [].concat(_toConsumableArray(locks), [lock]); targetElement.ontouchstart = function (event) { if (event.targetTouches.length === 1) { // detect single touch initialClientY = event.targetTouches[0].clientY; } }; targetElement.ontouchmove = function (event) { if (event.targetTouches.length === 1) { // detect single touch handleScroll(event, targetElement); } }; if (!documentListenerAdded) { document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); documentListenerAdded = true; } } } else { setOverflowHidden(options); var _lock = { targetElement: targetElement, options: options || {} }; locks = [].concat(_toConsumableArray(locks), [_lock]); } }; var clearAllBodyScrollLocks = exports.clearAllBodyScrollLocks = function clearAllBodyScrollLocks() { if (isIosDevice) { // Clear all locks ontouchstart/ontouchmove handlers, and the references locks.forEach(function (lock) { lock.targetElement.ontouchstart = null; lock.targetElement.ontouchmove = null; }); if (documentListenerAdded) { document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); documentListenerAdded = false; } locks = []; // Reset initial clientY initialClientY = -1; } else { restoreOverflowSetting(); locks = []; } }; var enableBodyScroll = exports.enableBodyScroll = function enableBodyScroll(targetElement) { if (isIosDevice) { if (!targetElement) { // eslint-disable-next-line no-console console.error('enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.'); return; } targetElement.ontouchstart = null; targetElement.ontouchmove = null; locks = locks.filter(function (lock) { return lock.targetElement !== targetElement; }); if (documentListenerAdded && locks.length === 0) { document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); documentListenerAdded = false; } } else if (locks.length === 1 && locks[0].targetElement === targetElement) { restoreOverflowSetting(); locks = []; } else { locks = locks.filter(function (lock) { return lock.targetElement !== targetElement; }); } }; });