@fastkit/body-scroll-lock
Version:
@fastkit/body-scroll-lock
1 lines • 12.9 kB
Source Map (JSON)
{"version":3,"sources":["../src/body-scroll-lock.ts"],"names":[],"mappings":"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","file":"body-scroll-lock.mjs","sourcesContent":["import { IN_WINDOW } from '@fastkit/helpers';\n\nexport interface BodyScrollOptions {\n reserveScrollBarGap?: boolean;\n allowTouchMove?: (el: EventTarget) => boolean;\n}\n\ninterface Lock {\n targetElement: any;\n options: BodyScrollOptions;\n}\n\nconst isIosDevice =\n IN_WINDOW &&\n !!window.navigator &&\n !!window.navigator.platform &&\n /iP(ad|hone|od)/.test(window.navigator.platform);\ntype HandleScrollEvent = TouchEvent;\n\nlet locks: Array<Lock> = [];\nlet documentListenerAdded = false;\nlet initialClientY = -1;\nlet previousBodyOverflowSetting: string | undefined;\nlet previousBodyPaddingRight: string | undefined;\n\n// returns true if `el` should be allowed to receive touchmove events.\nconst allowTouchMove = (el: EventTarget): boolean =>\n locks.some((lock) => {\n if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) {\n return true;\n }\n\n return false;\n });\n\nconst preventDefault = (rawEvent: HandleScrollEvent): boolean => {\n const e = rawEvent || window.event;\n\n // For the case whereby consumers adds a touchmove event listener to document.\n // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false })\n // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then\n // the touchmove event on document will break.\n if (allowTouchMove((e as any).target)) {\n return true;\n }\n\n // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom).\n if (e.touches.length > 1) return true;\n\n if (e.preventDefault) e.preventDefault();\n\n return false;\n};\n\nconst setOverflowHidden = (options?: BodyScrollOptions) => {\n // Setting overflow on body/documentElement synchronously in Desktop Safari slows down\n // the responsiveness for some reason. Setting within a setTimeout fixes this.\n setTimeout(() => {\n // If previousBodyPaddingRight is already set, don't set it again.\n if (previousBodyPaddingRight === undefined) {\n const reserveScrollBarGap =\n !!options && options.reserveScrollBarGap === true;\n const scrollBarGap =\n window.innerWidth - document.documentElement.clientWidth;\n\n if (reserveScrollBarGap && scrollBarGap > 0) {\n previousBodyPaddingRight = document.body.style.paddingRight;\n document.body.style.paddingRight = `${scrollBarGap}px`;\n }\n }\n\n // If previousBodyOverflowSetting is already set, don't set it again.\n if (previousBodyOverflowSetting === undefined) {\n previousBodyOverflowSetting = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n }\n });\n};\n\nconst restoreOverflowSetting = () => {\n // Setting overflow on body/documentElement synchronously in Desktop Safari slows down\n // the responsiveness for some reason. Setting within a setTimeout fixes this.\n setTimeout(() => {\n if (previousBodyPaddingRight !== undefined) {\n document.body.style.paddingRight = previousBodyPaddingRight;\n\n // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it\n // can be set again.\n previousBodyPaddingRight = undefined;\n }\n\n if (previousBodyOverflowSetting !== undefined) {\n document.body.style.overflow = previousBodyOverflowSetting;\n\n // Restore previousBodyOverflowSetting to undefined\n // so setOverflowHidden knows it can be set again.\n previousBodyOverflowSetting = undefined;\n }\n });\n};\n\n// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions\nconst isTargetElementTotallyScrolled = (targetElement: any): boolean =>\n targetElement\n ? targetElement.scrollHeight - targetElement.scrollTop <=\n targetElement.clientHeight\n : false;\n\nconst handleScroll = (\n event: HandleScrollEvent,\n targetElement: any,\n): boolean => {\n const clientY = event.targetTouches[0].clientY - initialClientY;\n\n if (allowTouchMove(event.target as any)) {\n return false;\n }\n\n if (targetElement && targetElement.scrollTop === 0 && clientY > 0) {\n // element is at the top of its scroll.\n return preventDefault(event);\n }\n\n if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) {\n // element is at the top of its scroll.\n return preventDefault(event);\n }\n\n event.stopPropagation();\n return true;\n};\n\nexport const disableBodyScroll = (\n targetElement: any,\n options?: BodyScrollOptions,\n): void => {\n if (!IN_WINDOW) return;\n\n if (isIosDevice) {\n // targetElement must be provided, and disableBodyScroll must not have been\n // called on this targetElement before.\n if (!targetElement) {\n // eslint-disable-next-line no-console\n console.error(\n 'disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.',\n );\n return;\n }\n\n if (\n targetElement &&\n !locks.some((lock) => lock.targetElement === targetElement)\n ) {\n const lock = {\n targetElement,\n options: options || {},\n };\n\n locks = [...locks, lock];\n\n targetElement.ontouchstart = (event: HandleScrollEvent) => {\n if (event.targetTouches.length === 1) {\n // detect single touch.\n initialClientY = event.targetTouches[0].clientY;\n }\n };\n targetElement.ontouchmove = (event: HandleScrollEvent) => {\n if (event.targetTouches.length === 1) {\n // detect single touch.\n handleScroll(event, targetElement);\n }\n };\n\n if (!documentListenerAdded) {\n document.addEventListener('touchmove', preventDefault, {\n passive: false,\n });\n documentListenerAdded = true;\n }\n }\n } else {\n setOverflowHidden(options);\n const lock = {\n targetElement,\n options: options || {},\n };\n\n locks = [...locks, lock];\n }\n};\n\nexport const clearAllBodyScrollLocks = (): void => {\n if (!IN_WINDOW) return;\n\n if (isIosDevice) {\n // Clear all locks ontouchstart/ontouchmove handlers, and the references.\n locks.forEach((lock: Lock) => {\n lock.targetElement.ontouchstart = null;\n lock.targetElement.ontouchmove = null;\n });\n\n if (documentListenerAdded) {\n document.removeEventListener(\n 'touchmove',\n preventDefault as any,\n { passive: false } as any,\n );\n documentListenerAdded = false;\n }\n\n locks = [];\n\n // Reset initial clientY.\n initialClientY = -1;\n } else {\n restoreOverflowSetting();\n locks = [];\n }\n};\n\nexport const enableBodyScroll = (targetElement: any): void => {\n if (!IN_WINDOW) return;\n\n if (isIosDevice) {\n if (!targetElement) {\n // eslint-disable-next-line no-console\n console.error(\n 'enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.',\n );\n return;\n }\n\n targetElement.ontouchstart = null;\n targetElement.ontouchmove = null;\n\n locks = locks.filter((lock) => lock.targetElement !== targetElement);\n\n if (documentListenerAdded && locks.length === 0) {\n document.removeEventListener(\n 'touchmove',\n preventDefault as any,\n { passive: false } as any,\n );\n\n documentListenerAdded = false;\n }\n } else {\n locks = locks.filter((lock) => lock.targetElement !== targetElement);\n if (!locks.length) {\n restoreOverflowSetting();\n }\n }\n};\n"]}