scroll.ts 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import { isClient } from '@vueuse/core'
  2. import { getStyle } from './style'
  3. export const isScroll = (el: HTMLElement, isVertical?: boolean): boolean => {
  4. if (!isClient) return false
  5. const key = (
  6. {
  7. undefined: 'overflow',
  8. true: 'overflow-y',
  9. false: 'overflow-x',
  10. } as const
  11. )[String(isVertical)]!
  12. const overflow = getStyle(el, key)
  13. return ['scroll', 'auto', 'overlay'].some(s => overflow.includes(s))
  14. }
  15. export const getScrollContainer = (el: HTMLElement, isVertical?: boolean): Window | HTMLElement | undefined => {
  16. if (!isClient) return
  17. let parent: HTMLElement = el
  18. while (parent) {
  19. if ([window, document, document.documentElement].includes(parent)) return window
  20. if (isScroll(parent, isVertical)) return parent
  21. parent = parent.parentNode as HTMLElement
  22. }
  23. return parent
  24. }
  25. let scrollBarWidth: number
  26. export const getScrollBarWidth = (namespace: string): number => {
  27. if (!isClient) return 0
  28. if (scrollBarWidth !== undefined) return scrollBarWidth
  29. const outer = document.createElement('div')
  30. outer.className = `${namespace}-scrollbar__wrap`
  31. outer.style.visibility = 'hidden'
  32. outer.style.width = '100px'
  33. outer.style.position = 'absolute'
  34. outer.style.top = '-9999px'
  35. document.body.appendChild(outer)
  36. const widthNoScroll = outer.offsetWidth
  37. outer.style.overflow = 'scroll'
  38. const inner = document.createElement('div')
  39. inner.style.width = '100%'
  40. outer.appendChild(inner)
  41. const widthWithScroll = inner.offsetWidth
  42. outer.parentNode?.removeChild(outer)
  43. scrollBarWidth = widthNoScroll - widthWithScroll
  44. return scrollBarWidth
  45. }
  46. /**
  47. * Scroll with in the container element, positioning the **selected** element at the top
  48. * of the container
  49. */
  50. export function scrollIntoView(container: HTMLElement, selected: HTMLElement): void {
  51. if (!isClient) return
  52. if (!selected) {
  53. container.scrollTop = 0
  54. return
  55. }
  56. const offsetParents: HTMLElement[] = []
  57. let pointer = selected.offsetParent
  58. while (pointer !== null && container !== pointer && container.contains(pointer)) {
  59. offsetParents.push(pointer as HTMLElement)
  60. pointer = (pointer as HTMLElement).offsetParent
  61. }
  62. const top = selected.offsetTop + offsetParents.reduce((prev, curr) => prev + curr.offsetTop, 0)
  63. const bottom = top + selected.offsetHeight
  64. const viewRectTop = container.scrollTop
  65. const viewRectBottom = viewRectTop + container.clientHeight
  66. if (top < viewRectTop) {
  67. container.scrollTop = top
  68. } else if (bottom > viewRectBottom) {
  69. container.scrollTop = bottom - container.clientHeight
  70. }
  71. }