Skip to content

⬅️ Back to Table of Contents

πŸ“„ useScrollLock

πŸ“Š Analysis Summary

Metric Count
πŸ”§ Functions 5
πŸ“¦ Imports 11
πŸ“Š Variables & Constants 1
🟒 Vue Composition API 2

πŸ“š Table of Contents

πŸ› οΈ File Location:

πŸ“‚ packages/core/useScrollLock/index.ts

πŸ“¦ Imports

Name Source
Fn @vueuse/shared
MaybeRefOrGetter vue
isIOS @vueuse/shared
toRef @vueuse/shared
tryOnScopeDispose @vueuse/shared
computed vue
shallowRef vue
toValue vue
watch vue
resolveElement ../_resolve-element
useEventListener ../useEventListener

Variables & Constants

Name Type Kind Value Exported
elInitialOverflow WeakMap<HTMLElement, string> const new WeakMap<HTMLElement, CSSStyleDeclaration['overflow']>() βœ—

Vue Composition API

Name Type Reactive Variables Composables
watch watch none none
computed computed none none

Functions

useScrollLock(element: MaybeRefOrGetter<HTMLElement | SVGEleme…, initialState: boolean): any

Lock scrolling of the element.

Parameters:

  • element any: No description

See: https://vueuse.org/useScrollLock

Raw JSDoc
/**
 * Lock scrolling of the element.
 *
 * @see https://vueuse.org/useScrollLock
 * @param element
 */

Calls:

  • shallowRef (from vue)
  • watch (from vue)
  • toRef (from @vueuse/shared)
  • resolveElement (from ../_resolve-element)
  • toValue (from vue)
  • elInitialOverflow.get
  • elInitialOverflow.set
  • useEventListener (from ../useEventListener)
  • preventDefault
  • stopTouchMoveListener
  • elInitialOverflow.delete
  • tryOnScopeDispose (from @vueuse/shared)
  • computed (from vue)
  • lock
  • unlock
Code
export function useScrollLock(
  element: MaybeRefOrGetter<HTMLElement | SVGElement | Window | Document | null | undefined>,
  initialState = false,
) {
  const isLocked = shallowRef(initialState)
  let stopTouchMoveListener: Fn | null = null
  let initialOverflow: CSSStyleDeclaration['overflow'] = ''

  watch(toRef(element), (el) => {
    const target = resolveElement(toValue(el))
    if (target) {
      const ele = target as HTMLElement
      if (!elInitialOverflow.get(ele))
        elInitialOverflow.set(ele, ele.style.overflow)

      if (ele.style.overflow !== 'hidden')
        initialOverflow = ele.style.overflow

      if (ele.style.overflow === 'hidden')
        return isLocked.value = true

      if (isLocked.value)
        return ele.style.overflow = 'hidden'
    }
  }, {
    immediate: true,
  })

  const lock = () => {
    const el = resolveElement(toValue(element))
    if (!el || isLocked.value)
      return
    if (isIOS) {
      stopTouchMoveListener = useEventListener(
        el,
        'touchmove',
        (e) => { preventDefault(e as TouchEvent) },
        { passive: false },
      )
    }
    el.style.overflow = 'hidden'
    isLocked.value = true
  }

  const unlock = () => {
    const el = resolveElement(toValue(element))
    if (!el || !isLocked.value)
      return
    if (isIOS)
      stopTouchMoveListener?.()
    el.style.overflow = initialOverflow
    elInitialOverflow.delete(el as HTMLElement)
    isLocked.value = false
  }

  tryOnScopeDispose(unlock)

  return computed<boolean>({
    get() {
      return isLocked.value
    },
    set(v) {
      if (v)
        lock()
      else unlock()
    },
  })
}

checkOverflowScroll(ele: Element): boolean

Parameters:

  • ele Element

Returns: boolean

Calls:

  • window.getComputedStyle
  • checkOverflowScroll
Code
function checkOverflowScroll(ele: Element): boolean {
  const style = window.getComputedStyle(ele)
  if (
    style.overflowX === 'scroll'
    || style.overflowY === 'scroll'
    || (style.overflowX === 'auto' && ele.clientWidth < ele.scrollWidth)
    || (style.overflowY === 'auto' && ele.clientHeight < ele.scrollHeight)
  ) {
    return true
  }
  else {
    const parent = ele.parentNode as Element

    if (!parent || parent.tagName === 'BODY')
      return false

    return checkOverflowScroll(parent)
  }
}

preventDefault(rawEvent: TouchEvent): boolean

Parameters:

  • rawEvent TouchEvent

Returns: boolean

Calls:

  • checkOverflowScroll
  • e.preventDefault

Internal Comments:

// Do not prevent if element or parentNodes have overflow: scroll set.
// Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom).

Code
function preventDefault(rawEvent: TouchEvent): boolean {
  const e = rawEvent || window.event

  const _target = e.target as Element

  // Do not prevent if element or parentNodes have overflow: scroll set.
  if (checkOverflowScroll(_target))
    return false

  // 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
}

Internal helpers

Declared inside another function in this file.

lock(): void

Returns: void

Calls:

  • resolveElement (from ../_resolve-element)
  • toValue (from vue)
  • useEventListener (from ../useEventListener)
  • preventDefault
Code
() => {
    const el = resolveElement(toValue(element))
    if (!el || isLocked.value)
      return
    if (isIOS) {
      stopTouchMoveListener = useEventListener(
        el,
        'touchmove',
        (e) => { preventDefault(e as TouchEvent) },
        { passive: false },
      )
    }
    el.style.overflow = 'hidden'
    isLocked.value = true
  }

unlock(): void

Returns: void

Calls:

  • resolveElement (from ../_resolve-element)
  • toValue (from vue)
  • stopTouchMoveListener
  • elInitialOverflow.delete
Code
() => {
    const el = resolveElement(toValue(element))
    if (!el || !isLocked.value)
      return
    if (isIOS)
      stopTouchMoveListener?.()
    el.style.overflow = initialOverflow
    elInitialOverflow.delete(el as HTMLElement)
    isLocked.value = false
  }

Generated by Syntax Scribe