Vue Example: Scroll Padding

vue
<template>
  <div>
    <div>
      <button @click="rowVirtualizer.scrollToIndex(40)">
        Scroll to index 40
      </button>
      <button @click="rowVirtualizer.scrollToIndex(20)">
        Then scroll to index 20
      </button>
    </div>

    <br />
    <br />

    <div
      ref="parentRef"
      class="List"
      style="height: 200px; width: 400px; overflow: auto"
    >
      <table :style="{ height: `${totalSize}px`, width: '100%' }">
        <thead ref="theadRef">
          <tr>
            <th>Index</th>
            <th>Key</th>
          </tr>
        </thead>
        <tbody>
          <tr
            v-for="virtualRow in virtualRows"
            :key="virtualRow.index"
            :class="virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'"
            :style="{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              height: `${virtualRow.size}px`,
              transform: `translateY(${virtualRow.start}px)`,
            }"
          >
            <td>#{{ virtualRow.index }}</td>
            <td>{{ virtualRow.key }}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { useElementSize } from '@vueuse/core'
import { useVirtualizer } from '@tanstack/vue-virtual'

const parentRef = ref<HTMLElement | null>(null)

const theadRef = ref<HTMLElement | null>(null)

const { height } = useElementSize(theadRef)

const rowVirtualizerOptions = computed(() => {
  return {
    count: 10000,
    getScrollElement: () => parentRef.value,
    estimateSize: () => 35,
    overscan: 5,
    paddingStart: height.value ?? 0,
    scrollPaddingStart: height.value ?? 0,
  }
})

const rowVirtualizer = useVirtualizer(rowVirtualizerOptions)

const virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())

const totalSize = computed(() => rowVirtualizer.value.getTotalSize())
</script>
<template>
  <div>
    <div>
      <button @click="rowVirtualizer.scrollToIndex(40)">
        Scroll to index 40
      </button>
      <button @click="rowVirtualizer.scrollToIndex(20)">
        Then scroll to index 20
      </button>
    </div>

    <br />
    <br />

    <div
      ref="parentRef"
      class="List"
      style="height: 200px; width: 400px; overflow: auto"
    >
      <table :style="{ height: `${totalSize}px`, width: '100%' }">
        <thead ref="theadRef">
          <tr>
            <th>Index</th>
            <th>Key</th>
          </tr>
        </thead>
        <tbody>
          <tr
            v-for="virtualRow in virtualRows"
            :key="virtualRow.index"
            :class="virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'"
            :style="{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              height: `${virtualRow.size}px`,
              transform: `translateY(${virtualRow.start}px)`,
            }"
          >
            <td>#{{ virtualRow.index }}</td>
            <td>{{ virtualRow.key }}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { useElementSize } from '@vueuse/core'
import { useVirtualizer } from '@tanstack/vue-virtual'

const parentRef = ref<HTMLElement | null>(null)

const theadRef = ref<HTMLElement | null>(null)

const { height } = useElementSize(theadRef)

const rowVirtualizerOptions = computed(() => {
  return {
    count: 10000,
    getScrollElement: () => parentRef.value,
    estimateSize: () => 35,
    overscan: 5,
    paddingStart: height.value ?? 0,
    scrollPaddingStart: height.value ?? 0,
  }
})

const rowVirtualizer = useVirtualizer(rowVirtualizerOptions)

const virtualRows = computed(() => rowVirtualizer.value.getVirtualItems())

const totalSize = computed(() => rowVirtualizer.value.getTotalSize())
</script>
Subscribe to Bytes

Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.

Bytes

No spam. Unsubscribe at any time.