PageSourceSearch

https://quasar.dev/a/use-scroll.js

js quasar.dev collected 2026-10-02 03:49:04 UTC 12,062 bytes, 64 lines download raw bytes

1const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["a/useScroll.r.js","a/rolldown-runtime.js","a/v.js","a/q.js","a/q.css","a/AppFullscreen.r.js","a/useScroll.css","a/useScroll.s.js"])))=>i.map(i=>d[i]);
2import{H as e,T as t,U as n,Z as r,ft as i,p as a,v as o,w as s,y as c}from"./v.js";import{a as l}from"./index.js";import{t as u}from"./DocCode.js";import"./DocCopyBtn.js";import{t as d}from"./page-utils.js";import{t as f}from"./DocPage.js";import{t as p}from"./DocLink.js";import{t as m}from"./DocPrerender.js";import{t as h}from"./DocExample.js";var g={__name:`use-scroll`,setup(g){n(`_q_ex`,{name:`useScroll`,runtime:l(()=>import(`./useScroll.r.js`).then(e=>e.t),__vite__mapDeps([0,1,2,3,4,5,6])),source:()=>l(()=>import(`./useScroll.s.js`).then(e=>e.t),__vite__mapDeps([7,1]))});let _=[{name:`Scroll Observer`,category:`Vue Components`,path:`/vue-components/scroll-observer`},{name:`v-scroll`,category:`Vue Directives`,path:`/vue-directives/scroll`},{name:`v-scroll-fire`,category:`Vue Directives`,path:`/vue-directives/scroll-fire`}],v=[{name:`useMutation`,category:`Vue Composables`,path:`/vue-composables/use-mutation`,classes:`doc-page__related--left`},{name:`useSoftFullscreen`,category:`Vue Composables`,path:`/vue-composables/use-soft-fullscreen`,classes:`doc-page__related--right`}],y=[{id:`syntax`,title:`2. Syntax`},{id:`which-container-gets-tracked`,title:`3. Which container gets tracked`},{id:`changing-the-options-while-running`,title:`4. Changing the options while running`},{id:`example`,title:`5. Example`}];return(n,l)=>(e(),c(f,{title:`useScroll composable`,desc:`What is useScroll() composable and how you can use it`,badge:`v2.34+`,heading:``,"edit-link":`vue-composables/use-scroll`,"md-link":``,toc:y,related:_,nav:v},{default:r(()=>[l[26]||=o(`p`,null,[s(`The `),o(`code`,{class:`doc-token`},`useScroll()`),s(` composable tracks the scrolling of the page (or of a scrollable container) through reactive state: the `),o(`code`,{class:`doc-token`},`scrollPosition`),s(`, the `),o(`code`,{class:`doc-token`},`scrollDirection`),s(` of the last scroll, the `),o(`code`,{class:`doc-token`},`scrollDelta`),s(` since the previous report and the `),o(`code`,{class:`doc-token`},`scrollInflectionPoint`),s(` where the direction last changed.`)],-1),o(`p`,null,[l[10]||=s(`It is the setup-code counterpart of the `,-1),t(p,{to:`/vue-components/scroll-observer`},{default:r(()=>[...l[8]||=[s(`QScrollObserver`,-1)]]),_:1}),l[11]||=s(` component, which is built on it, and of the `,-1),t(p,{to:`/vue-directives/scroll`},{default:r(()=>[...l[9]||=[s(`v-scroll`,-1)]]),_:1}),l[12]||=s(` directive. Use the composable when you want the scroll details on your component, or on any scrollable container, without adding an extra node to your template.`,-1)]),l[27]||=o(`div`,{class:`doc-note doc-note--note`},[o(`div`,{class:`doc-note__title`},`NOTE`),o(`p`,null,`On the server-side of SSR or SSG modes, the composable never listens to anything: the state keeps its initial values until the client takes over.`)],-1),l[28]||=o(`div`,{class:`doc-note doc-note--tip`},[o(`div`,{class:`doc-note__title`},`Outside of a component`),o(`p`,null,[s(`The composable can also be called outside of `),o(`code`,{class:`doc-token`},`setup()`),s(`: in a boot file, a store or a plain module. There is no component root to start the detection from and no mount to wait for, so supply a `),o(`code`,{class:`doc-token`},`scrollTarget`),s(` (or a `),o(`code`,{class:`doc-token`},`target`),s(` element); the tracking starts right away and nothing stops it by itself: call `),o(`code`,{class:`doc-token`},`stopScroll()`),s(` when you are done.`)])],-1),o(`h2`,{id:`syntax`,class:`doc-heading doc-h2`,onClick:l[1]||=e=>i(d)(`syntax`)},[l[13]||=s(`Syntax`,-1),o(`a`,{class:`doc-heading__anchor`,href:`#syntax`,"aria-label":`Copy anchor to Syntax`,onClick:l[0]||=a(e=>i(d)(`syntax`),[`prevent`,`stop`])})]),t(i(m),null,{default:r(()=>[t(u,{lang:`js`,code:`import { useTemplateRef } from 'vue'
3import { useScroll } from 'quasar'
4
5setup () {
6  const scrollTarget = useTemplateRef('scrollTarget') // an Element or a component
7
8  const {
9    scrollPosition, scrollDirection, scrollDirectionChanged,
10    scrollDelta, scrollInflectionPoint, refreshScroll, stopScroll
11  } = useScroll({
12    // all optional:
13    scrollTarget,        // the scroll container (default: auto detected from the component's own root element)
14    axis: 'both',        // 'vertical', 'horizontal' or 'both' (default: 'vertical')
15    debounce: 100,       // ms per report; 0 for one on every scroll event (default: one per animation frame)
16    disabled: true,      // pause listening (default: false)
17    onScroll (details) { // called with the scroll details on every change
18      // ...
19    }
20  })
21
22  // ...
23}`})]),_:1}),t(i(m),null,{default:r(()=>[t(u,{lang:`ts`,code:`function useScroll(
24  options?: MaybeRefOrGetter<{
25    target?: MaybeRefOrGetter<
26      Element | ComponentPublicInstance | null | undefined
27    >
28    scrollTarget?: MaybeRefOrGetter<
29      Element | Window | string | ComponentPublicInstance | null | undefined
30    >
31    axis?: 'vertical' | 'horizontal' | 'both'
32    debounce?: string | number
33    disabled?: boolean
34    onScroll?: (details: {
35      position: { top: number; left: number }
36      direction: 'up' | 'down' | 'left' | 'right'
37      directionChanged: boolean
38      delta: { top: number; left: number }
39      inflectionPoint: { top: number; left: number }
40    }) => void
41  }>
42): {
43  scrollPosition: ShallowRef<{ top: number; left: number }>
43
44  scrollDirection: Ref<'up' | 'down' | 'left' | 'right'>
45  scrollDirectionChanged: Ref<boolean>
46  scrollDelta: ShallowRef<{ top: number; left: number }>
47  scrollInflectionPoint: ShallowRef<{ top: number; left: number }>
48  refreshScroll: () => void
49  stopScroll: () => void
50}`})]),_:1}),l[29]||=o(`p`,null,[s(`The reactive state mirrors the details that QScrollObserver emits (and that `),o(`code`,{class:`doc-token`},`onScroll`),s(` receives): `),o(`code`,{class:`doc-token`},`scrollPosition`),s(`, `),o(`code`,{class:`doc-token`},`scrollDelta`),s(` and `),o(`code`,{class:`doc-token`},`scrollInflectionPoint`),s(` are Objects with `),o(`code`,{class:`doc-token`},`top`),s(` and `),o(`code`,{class:`doc-token`},`left`),s(` offsets (in pixels), `),o(`code`,{class:`doc-token`},`scrollDirection`),s(` is the direction of the last scroll and `),o(`code`,{class:`doc-token`},`scrollDirectionChanged`),s(` tells whether that last scroll reversed the direction.`)],-1),o(`h2`,{id:`which-container-gets-tracked`,class:`doc-heading doc-h2`,onClick:l[3]||=e=>i(d)(`which-container-gets-tracked`)},[l[14]||=s(`Which container gets tracked`,-1),o(`a`,{class:`doc-heading__anchor`,href:`#which-container-gets-tracked`,"aria-label":`Copy anchor to Which container gets tracked`,onClick:l[2]||=a(e=>i(d)(`which-container-gets-tracked`),[`prevent`,`stop`])})]),o(`p`,null,[l[16]||=s(`Without options, the composable follows the `,-1),t(p,{to:`/vue-components/scroll-observer#determining-scrolling-container`},{default:r(()=>[...l[15]||=[s(`same algorithm`,-1)]]),_:1}),l[17]||=s(` as every scrolling component and directive of Quasar: starting from the root element of the component it is called in (as of the moment the component gets mounted), it looks for the closest parent with the `,-1),l[18]||=o(`code`,{class:`doc-token`},`scroll`,-1),l[19]||=s(`, `,-1),l[20]||=o(`code`,{class:`doc-token`},`scroll-y`,-1),l[21]||=s(` or `,-1),l[22]||=o(`code`,{class:`doc-token`},`overflow-auto`,-1),l[23]||=s(` CSS class and, if none is found, it listens to the page itself.`,-1)]),l[30]||=o(`ul`,null,[o(`li`,null,[o(`code`,{class:`doc-token`},`scrollTarget`),s(` names the container directly: an Element (or `),o(`code`,{class:`doc-token`},`window`),s(`), a CSS selector, or a component instance (standing for its root element), the same as the `),o(`code`,{class:`doc-token`},`scroll-target`),s(` prop of the scrolling components.`)]),o(`li`,null,[o(`code`,{class:`doc-token`},`target`),s(` changes where the auto detection starts from: an element or component ref whose closest scrollable parent is the container you are after. This is what you need in a component rendering a fragment (multiple root nodes), which has no root element to start from.`)])],-1),l[31]||=o(`p`,null,[s(`The first report happens as soon as the container is available, should it be scrolled already; `),o(`code`,{class:`doc-token`},`onScroll`),s(` is called for it as well, then only when the position changes on the watched `),o(`code`,{class:`doc-token`},`axis`),s(`.`)],-1),l[32]||=o(`p`,null,[s(`Without a `),o(`code`,{class:`doc-token`},`debounce`),s(`, the composable reports at most once per animation frame, no matter how many scroll events the browser fires in between. With `),o(`code`,{class:`doc-token`},`debounce: 0`),s(` it reports on every scroll event. With a `),o(`code`,{class:`doc-token`},`debounce`),s(` of some milliseconds, it reports at most once per window of that many milliseconds; the last change is never missed.`)],-1),l[33]||=o(`p`,null,[o(`code`,{class:`doc-token`},`refreshScroll()`),s(` reads the position right away, skipping the debounce. You will rarely need it, since the browser reports every scroll on its own.`)],-1),l[34]||=o(`p`,null,[o(`code`,{class:`doc-token`},`stopScroll()`),s(` ends the tracking for good. You will rarely need it either, as the composable stops by itself when the component gets destroyed.`)],-1),o(`h2`,{id:`changing-the-options-while-running`,class:`doc-heading doc-h2`,onClick:l[5]||=e=>i(d)(`changing-the-options-while-running`)},[l[24]||=s(`Changing the options while running`,-1),o(`a`,{class:`doc-heading__anchor`,href:`#changing-the-options-while-running`,"aria-label":`Copy anchor to Changing the options while running`,onClick:l[4]||=a(e=>i(d)(`changing-the-options-while-running`),[`prevent`,`stop`])})]),l[35]||=o(`p`,null,`The options can be a plain Object, a Ref or a getter Function. A plain Object is read once. With a Ref or a getter, the composable tracks whatever reactive state the options read and re-applies them whenever that state changes, so you never call anything to “update” it:`,-1),l[36]||=o(`ul`,null,[o(`li`,null,[s(`toggling `),o(`code`,{class:`doc-token`},`disabled`),s(` pauses and resumes the tracking (the state keeps its last values while paused, and resuming reports right away if the position moved meanwhile)`)]),o(`li`,null,[s(`pointing `),o(`code`,{class:`doc-token`},`scrollTarget`),s(` (or `),o(`code`,{class:`doc-token`},`target`),s(`) to another container follows it, starting from scratch`)]),o(`li`,null,[s(`changing `),o(`code`,{class:`doc-token`},`axis`),s(` or `),o(`code`,{class:`doc-token`},`debounce`),s(` applies from the next scroll`)]),o(`li`,null,[s(`swapping `),o(`code`,{class:`doc-token`},`onScroll`),s(` takes effect from the next scroll`)])],-1),t(i(m),null,{default:r(()=>[t(u,{lang:`js`,code:`import { ref } from 'vue'
51import { useScroll } from 'quasar'
52
53setup () {
54  const paused = ref(false)
55
56  const { scrollPosition } = useScroll(() => ({
57    disabled: paused.value
58  }))
59
60  function pause () { paused.value = true }
61  function resume () { paused.value = false }
62
63  // ...
64}`})]),_:1}),o(`h2`,{id:`example`,class:`doc-heading doc-h2`,onClick:l[7]||=e=>i(d)(`example`)},[l[25]||=s(`Example`,-1),o(`a`,{class:`doc-heading__anchor`,href:`#example`,"aria-label":`Copy anchor to Example`,onClick:l[6]||=a(e=>i(d)(`example`),[`prevent`,`stop`])})]),l[37]||=o(`p`,null,`Tracking a scrollable container through a template ref, with every detail the composable reports:`,-1),t(h,{title:`Scrolling container`,file:`Container`}),l[38]||=o(`p`,null,`Without options, the auto detection starts from the component’s root element and, on a standard layout, ends up on the page itself. Scroll this page down past the example below and back up a bit: the “back to top” button gets enabled once the page has been scrolled down and the user is scrolling up again:`,-1),t(h,{title:`Page scroll`,file:`Page`})]),_:1}))}};export{g as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.