kliping.pro
  • Docs
  • Directory
  • Colors
  • Typeset
Hooks
  • State7
  • Advanced6
  • Lifecycle16
  • Browser21
    • useActiveElement
    • useBattery
    • useClickAnyWhere
    • useClickAway
    • useClipboard
    • useDocumentVisibility
    • useElementSize
    • useEventListener
    • useFps
    • useFullscreen
    • useHash
    • useHover
    • useInViewport
    • useIsMatchMedia
    • useIsOnline
    • useMouse
    • useNetwork
    • useOs
    • useScrollLock
    • useTextSelection
    • useTitle
  • Development1
  • Third-Party4
  • Libraries4

useScrollLock

PreviousNext

Mengunci gulungan halaman untuk dialog, drawer, dan menu layar penuh.

Mengunci gulungan halaman — untuk dialog, drawer, dan menu layar penuh.

Bagian yang paling sering dilupakan bukan penguncian, melainkan lebar yang melompat. Menyembunyikan bilah gulung membuat halaman melebar beberapa belas piksel, dan seluruh isinya bergeser tepat saat dialognya muncul. Karena itu lebar bilah gulungnya dihitung lalu dititipkan sebagai padding-right, dan halamannya diam.

Gaya aslinya disimpan sebelum diubah, bukan disetel ke nilai bawaan saat dilepas. Elemen yang sudah punya overflow atau padding-right sendiri mendapatkannya kembali utuh.

Installation ​

pnpm dlx shadcn-vue@latest add @kliping/use-scroll-lock

Perlu @kliping terdaftar di components.json project Anda. Lihat cara menyiapkannya.

Ikut terpasang sendiri: is-browser, try-on-mounted.

Salin dulu is-browser, try-on-mounted — masing-masing memuat sumbernya di halamannya sendiri — lalu tempel berkas ini.

ts
import { getCurrentScope, onScopeDispose, readonly, ref } from 'vue'
import { isBrowser } from '@/lib/is-browser'
import { tryOnMounted } from '@/lib/try-on-mounted'

export interface UseScrollLockOptions {
  /** Lock as soon as the composable is called. Default: true. */
  autoLock?: boolean
  /** Element to lock, or its CSS selector. Default: `document.body`. */
  lockTarget?: HTMLElement | string
  /** Add the scrollbar width as padding so the content does not shift. Default: true. */
  widthReflow?: boolean
}

export function useScrollLock(options: UseScrollLockOptions = {}) {
  const { autoLock = true, lockTarget, widthReflow = true } = options

  const isLocked = ref(false)
  let target: HTMLElement | null = null
  let originalStyle: { overflow: string; paddingRight: string } | null = null

  const findTarget = (): HTMLElement | null => {
    if (!isBrowser) return null
    if (typeof lockTarget === 'string') return document.querySelector<HTMLElement>(lockTarget)
    return lockTarget ?? document.body
  }

  const lock = () => {
    target ??= findTarget()
    if (!target || isLocked.value) return

    originalStyle = {
      overflow: target.style.overflow,
      paddingRight: target.style.paddingRight,
    }

    if (widthReflow) {
      const fullWidth = target === document.body ? window.innerWidth : target.offsetWidth

      const currentPadding = Number.parseInt(window.getComputedStyle(target).paddingRight, 10) || 0

      const scrollbarWidth = fullWidth - target.scrollWidth
      target.style.paddingRight = `${scrollbarWidth + currentPadding}px`
    }

    target.style.overflow = 'hidden'
    isLocked.value = true
  }

  const unlock = () => {
    if (target && originalStyle) {
      target.style.overflow = originalStyle.overflow
      if (widthReflow) target.style.paddingRight = originalStyle.paddingRight
    }
    originalStyle = null
    isLocked.value = false
  }

  tryOnMounted(() => {
    target = findTarget()
    if (autoLock) lock()
  })

  if (getCurrentScope()) onScopeDispose(unlock)

  return { isLocked: readonly(isLocked), lock, unlock }
}

API ​

ts
export interface UseScrollLockOptions {
  /** Lock as soon as the composable is called. Default: true. */
  autoLock?: boolean
  /** Element to lock, or its CSS selector. Default: `document.body`. */
  lockTarget?: HTMLElement | string
  /** Add the scrollbar width as padding so the content does not shift. Default: true. */
  widthReflow?: boolean
}

export function useScrollLock(options: UseScrollLockOptions = {})
PrevioususeOsNext useTextSelection

On This Page

InstallationAPI
Dirilis di bawah lisensi MIT. Ditulis ulang untuk Vue dari shadcn-vue, shadcn-ui-blocks, dan shadcn-hooks. Hak cipta © 2026 Kliping