@fastkit/body-scroll-lock
Advanced tools
Comparing version 0.6.10 to 0.6.11
@@ -5,10 +5,187 @@ 'use strict'; | ||
var helpers = require('@fastkit/helpers'); | ||
const isIosDevice = helpers.IN_WINDOW && | ||
!!window.navigator && | ||
!!window.navigator.platform && | ||
/iP(ad|hone|od)/.test(window.navigator.platform); | ||
let locks = []; | ||
let documentListenerAdded = false; | ||
let initialClientY = -1; | ||
let previousBodyOverflowSetting; | ||
let previousBodyPaddingRight; | ||
// returns true if `el` should be allowed to receive touchmove events. | ||
const allowTouchMove = (el) => locks.some((lock) => { | ||
if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) { | ||
return true; | ||
} | ||
return false; | ||
}); | ||
const preventDefault = (rawEvent) => { | ||
const 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; | ||
}; | ||
const 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(() => { | ||
// If previousBodyPaddingRight is already set, don't set it again. | ||
if (previousBodyPaddingRight === undefined) { | ||
const reserveScrollBarGap = !!options && options.reserveScrollBarGap === true; | ||
const 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'; | ||
} | ||
}); | ||
}; | ||
const restoreOverflowSetting = () => { | ||
// Setting overflow on body/documentElement synchronously in Desktop Safari slows down | ||
// the responsiveness for some reason. Setting within a setTimeout fixes this. | ||
setTimeout(() => { | ||
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 | ||
const isTargetElementTotallyScrolled = (targetElement) => targetElement | ||
? targetElement.scrollHeight - targetElement.scrollTop <= | ||
targetElement.clientHeight | ||
: false; | ||
const handleScroll = (event, targetElement) => { | ||
const 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; | ||
}; | ||
const disableBodyScroll = (targetElement, options) => { | ||
return; | ||
if (!helpers.IN_WINDOW) | ||
return; | ||
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((lock) => lock.targetElement === targetElement)) { | ||
const lock = { | ||
targetElement, | ||
options: options || {}, | ||
}; | ||
locks = [...locks, lock]; | ||
targetElement.ontouchstart = (event) => { | ||
if (event.targetTouches.length === 1) { | ||
// detect single touch. | ||
initialClientY = event.targetTouches[0].clientY; | ||
} | ||
}; | ||
targetElement.ontouchmove = (event) => { | ||
if (event.targetTouches.length === 1) { | ||
// detect single touch. | ||
handleScroll(event, targetElement); | ||
} | ||
}; | ||
if (!documentListenerAdded) { | ||
document.addEventListener('touchmove', preventDefault, { | ||
passive: false, | ||
}); | ||
documentListenerAdded = true; | ||
} | ||
} | ||
} | ||
else { | ||
setOverflowHidden(options); | ||
const lock = { | ||
targetElement, | ||
options: options || {}, | ||
}; | ||
locks = [...locks, lock]; | ||
} | ||
}; | ||
const clearAllBodyScrollLocks = () => { | ||
return; | ||
if (!helpers.IN_WINDOW) | ||
return; | ||
if (isIosDevice) { | ||
// Clear all locks ontouchstart/ontouchmove handlers, and the references. | ||
locks.forEach((lock) => { | ||
lock.targetElement.ontouchstart = null; | ||
lock.targetElement.ontouchmove = null; | ||
}); | ||
if (documentListenerAdded) { | ||
document.removeEventListener('touchmove', preventDefault, { passive: false }); | ||
documentListenerAdded = false; | ||
} | ||
locks = []; | ||
// Reset initial clientY. | ||
initialClientY = -1; | ||
} | ||
else { | ||
restoreOverflowSetting(); | ||
locks = []; | ||
} | ||
}; | ||
const enableBodyScroll = (targetElement) => { | ||
return; | ||
if (!helpers.IN_WINDOW) | ||
return; | ||
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((lock) => lock.targetElement !== targetElement); | ||
if (documentListenerAdded && locks.length === 0) { | ||
document.removeEventListener('touchmove', preventDefault, { passive: false }); | ||
documentListenerAdded = false; | ||
} | ||
} | ||
else { | ||
locks = locks.filter((lock) => lock.targetElement !== targetElement); | ||
if (!locks.length) { | ||
restoreOverflowSetting(); | ||
} | ||
} | ||
}; | ||
@@ -15,0 +192,0 @@ |
@@ -5,10 +5,187 @@ 'use strict'; | ||
var helpers = require('@fastkit/helpers'); | ||
const isIosDevice = helpers.IN_WINDOW && | ||
!!window.navigator && | ||
!!window.navigator.platform && | ||
/iP(ad|hone|od)/.test(window.navigator.platform); | ||
let locks = []; | ||
let documentListenerAdded = false; | ||
let initialClientY = -1; | ||
let previousBodyOverflowSetting; | ||
let previousBodyPaddingRight; | ||
// returns true if `el` should be allowed to receive touchmove events. | ||
const allowTouchMove = (el) => locks.some((lock) => { | ||
if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) { | ||
return true; | ||
} | ||
return false; | ||
}); | ||
const preventDefault = (rawEvent) => { | ||
const 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; | ||
}; | ||
const 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(() => { | ||
// If previousBodyPaddingRight is already set, don't set it again. | ||
if (previousBodyPaddingRight === undefined) { | ||
const reserveScrollBarGap = !!options && options.reserveScrollBarGap === true; | ||
const 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'; | ||
} | ||
}); | ||
}; | ||
const restoreOverflowSetting = () => { | ||
// Setting overflow on body/documentElement synchronously in Desktop Safari slows down | ||
// the responsiveness for some reason. Setting within a setTimeout fixes this. | ||
setTimeout(() => { | ||
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 | ||
const isTargetElementTotallyScrolled = (targetElement) => targetElement | ||
? targetElement.scrollHeight - targetElement.scrollTop <= | ||
targetElement.clientHeight | ||
: false; | ||
const handleScroll = (event, targetElement) => { | ||
const 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; | ||
}; | ||
const disableBodyScroll = (targetElement, options) => { | ||
return; | ||
if (!helpers.IN_WINDOW) | ||
return; | ||
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((lock) => lock.targetElement === targetElement)) { | ||
const lock = { | ||
targetElement, | ||
options: options || {}, | ||
}; | ||
locks = [...locks, lock]; | ||
targetElement.ontouchstart = (event) => { | ||
if (event.targetTouches.length === 1) { | ||
// detect single touch. | ||
initialClientY = event.targetTouches[0].clientY; | ||
} | ||
}; | ||
targetElement.ontouchmove = (event) => { | ||
if (event.targetTouches.length === 1) { | ||
// detect single touch. | ||
handleScroll(event, targetElement); | ||
} | ||
}; | ||
if (!documentListenerAdded) { | ||
document.addEventListener('touchmove', preventDefault, { | ||
passive: false, | ||
}); | ||
documentListenerAdded = true; | ||
} | ||
} | ||
} | ||
else { | ||
setOverflowHidden(options); | ||
const lock = { | ||
targetElement, | ||
options: options || {}, | ||
}; | ||
locks = [...locks, lock]; | ||
} | ||
}; | ||
const clearAllBodyScrollLocks = () => { | ||
return; | ||
if (!helpers.IN_WINDOW) | ||
return; | ||
if (isIosDevice) { | ||
// Clear all locks ontouchstart/ontouchmove handlers, and the references. | ||
locks.forEach((lock) => { | ||
lock.targetElement.ontouchstart = null; | ||
lock.targetElement.ontouchmove = null; | ||
}); | ||
if (documentListenerAdded) { | ||
document.removeEventListener('touchmove', preventDefault, { passive: false }); | ||
documentListenerAdded = false; | ||
} | ||
locks = []; | ||
// Reset initial clientY. | ||
initialClientY = -1; | ||
} | ||
else { | ||
restoreOverflowSetting(); | ||
locks = []; | ||
} | ||
}; | ||
const enableBodyScroll = (targetElement) => { | ||
return; | ||
if (!helpers.IN_WINDOW) | ||
return; | ||
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((lock) => lock.targetElement !== targetElement); | ||
if (documentListenerAdded && locks.length === 0) { | ||
document.removeEventListener('touchmove', preventDefault, { passive: false }); | ||
documentListenerAdded = false; | ||
} | ||
} | ||
else { | ||
locks = locks.filter((lock) => lock.targetElement !== targetElement); | ||
if (!locks.length) { | ||
restoreOverflowSetting(); | ||
} | ||
} | ||
}; | ||
@@ -15,0 +192,0 @@ |
@@ -1,2 +0,4 @@ | ||
const isIosDevice = (typeof document !== 'undefined') && | ||
import { IN_WINDOW } from '@fastkit/helpers'; | ||
const isIosDevice = IN_WINDOW && | ||
!!window.navigator && | ||
@@ -93,3 +95,3 @@ !!window.navigator.platform && | ||
const disableBodyScroll = (targetElement, options) => { | ||
if (!(typeof document !== 'undefined')) | ||
if (!IN_WINDOW) | ||
return; | ||
@@ -141,3 +143,3 @@ if (isIosDevice) { | ||
const clearAllBodyScrollLocks = () => { | ||
if (!(typeof document !== 'undefined')) | ||
if (!IN_WINDOW) | ||
return; | ||
@@ -164,3 +166,3 @@ if (isIosDevice) { | ||
const enableBodyScroll = (targetElement) => { | ||
if (!(typeof document !== 'undefined')) | ||
if (!IN_WINDOW) | ||
return; | ||
@@ -167,0 +169,0 @@ if (isIosDevice) { |
{ | ||
"name": "@fastkit/body-scroll-lock", | ||
"version": "0.6.10", | ||
"version": "0.6.11", | ||
"description": "@fastkit/body-scroll-lock", | ||
@@ -26,4 +26,4 @@ "main": "index.js", | ||
"dependencies": { | ||
"@fastkit/tiny-logger": "0.6.10" | ||
"@fastkit/tiny-logger": "0.6.11" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
24473
580
+ Added@fastkit/helpers@0.6.11(transitive)
+ Added@fastkit/tiny-logger@0.6.11(transitive)
- Removed@fastkit/helpers@0.6.10(transitive)
- Removed@fastkit/tiny-logger@0.6.10(transitive)
Updated@fastkit/tiny-logger@0.6.11