UNPKG

tua-body-scroll-lock

Version:

🔐Body scroll locking that just works with everything

126 lines (100 loc) 3.45 kB
import { getLockState } from './getLockState' import { handleScroll } from './handleScroll' import { setOverflowForMobile, setOverflowForPc } from './setOverflow' import type { BSLOptions, LockState, Nullable } from './types' import { isServer, detectOS, getPreventEventDefault, getEventListenerOptions, noticeRequiredTargetElement, toArray, } from './utils' /** * lock body scroll * @param targetElement the element(s) still needs scrolling(iOS only) * @param options */ export function lock (targetElement?: Nullable<HTMLElement>, options?: BSLOptions): void { if (isServer()) return noticeRequiredTargetElement(targetElement) const detectRes = detectOS() const lockState = getLockState(options) if (detectRes.ios) { toArray(targetElement) .filter(e => e && lockState.lockedElements.indexOf(e) === -1) .forEach((element) => { element.ontouchstart = (event) => { const { clientX, clientY } = event.targetTouches[0] lockState.initialClientPos = { clientX, clientY } } element.ontouchmove = (event) => { handleScroll(event, element, lockState.initialClientPos) } lockState.lockedElements.push(element) }) addTouchMoveListener(lockState) if (options?.setOverflowForIOS) { lockState.unLockCallback = setOverflowForPc(options) } } else if (lockState.lockedNum <= 0) { lockState.unLockCallback = detectRes.android ? setOverflowForMobile(options) : setOverflowForPc(options) } lockState.lockedNum += 1 } /** * unlock body scroll * @param targetElement the element(s) still needs scrolling(iOS only) * @param options */ export function unlock (targetElement?: Nullable<HTMLElement>, options?: BSLOptions): void { if (isServer()) return noticeRequiredTargetElement(targetElement) const lockState = getLockState(options) lockState.lockedNum -= 1 if (lockState.lockedNum > 0) return lockState.unLockCallback?.() if (!detectOS().ios) return toArray(targetElement).forEach((element) => { const index = lockState.lockedElements.indexOf(element) if (element && index !== -1) { element.ontouchmove = null element.ontouchstart = null lockState.lockedElements.splice(index, 1) } }) removeTouchMoveListener(lockState) } /** * clear all body locks * @param options */ export function clearBodyLocks (options?: BSLOptions): void { if (isServer()) return const lockState = getLockState(options) lockState.lockedNum = 0 lockState.unLockCallback?.() if (!detectOS().ios) return if (lockState.lockedElements.length) { let element = lockState.lockedElements.pop() while (element) { element.ontouchmove = null element.ontouchstart = null element = lockState.lockedElements.pop() } } removeTouchMoveListener(lockState) } export function addTouchMoveListener (lockState: LockState) { if (!detectOS().ios) return if (lockState.documentListenerAdded) return document.addEventListener('touchmove', getPreventEventDefault(), getEventListenerOptions({ passive: false })) lockState.documentListenerAdded = true } export function removeTouchMoveListener (lockState: LockState) { if (!lockState.documentListenerAdded) return document.removeEventListener('touchmove', getPreventEventDefault(), getEventListenerOptions({ passive: false })) lockState.documentListenerAdded = false }