Table
Standard HTML <table> virtualization
Demonstrates usage of custom tags (table, tbody, tr) for semantically correct and accessible tabular data virtualization with 1,000 items. Row height is fixed at 0px.
| ID | Name | Age | City | Role | Status | |
|---|---|---|---|---|---|---|
| #0 | User 0 | user0@example.com | 20 | city1 | Admin | Active |
| #1 | User 1 | user1@example.com | 27 | city2 | Editor | Inactive |
| #2 | User 2 | user2@example.com | 34 | city3 | Viewer | Active |
| #3 | User 3 | user3@example.com | 41 | city4 | Admin | Inactive |
| #4 | User 4 | user4@example.com | 48 | city5 | Editor | Active |
<script setup lang="ts">
import type { Ref } from 'vue';
import { VirtualScroll } from '@pdanpdan/virtual-scroll';
import { computed, inject, ref } from 'vue';
import ExampleContainer from '#/components/ExampleContainer.vue';
import ScrollControls from '#/components/ScrollControls.vue';
import ScrollStatus from '#/components/ScrollStatus.vue';
import { useExampleScroll } from '#/lib/useExampleScroll';
import { html as highlightedCode } from './+Page.vue?highlight';
const itemCount = ref(1000);
const itemSize = ref(0);
const bufferBefore = ref(5);
const bufferAfter = ref(5);
const stickyHeader = ref(true);
const stickyFooter = ref(false);
const items = computed(() => Array.from({ length: itemCount.value }, (_, i) => ({
id: i,
name: `User ${ i }`,
email: `user${ i }@example.com`,
role: i % 3 === 0 ? 'Admin' : (i % 3 === 1 ? 'Editor' : 'Viewer'),
status: i % 2 === 0 ? 'Active' : 'Inactive',
age: 20 + (i * 7) % 60,
city: `city${ 1 + i % 5 }`,
})));
const {
virtualScrollRef,
scrollDetails,
onScroll,
handleScrollToIndex,
handleScrollToOffset,
} = useExampleScroll();
const debugMode = inject<Ref<boolean>>('debugMode', ref(false));
</script>
<template>
<ExampleContainer :code="highlightedCode">
<template #title>
<span class="example-title example-title--group-2">Table</span>
</template>
<template #description>
Demonstrates usage of custom tags (<strong>table</strong>, <strong>tbody</strong>, <strong>tr</strong>) for semantically correct and accessible tabular data virtualization with {{ itemCount.toLocaleString() }} items. Row height is fixed at {{ itemSize }}px.
</template>
<template #icon>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="example-icon example-icon--group-2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75h16.5v16.5H3.75V3.75ZM12 3.75v16.5M3.75 12h16.5" />
</svg>
</template>
<template #subtitle>
Standard HTML <strong><table></strong> virtualization
</template>
<template #controls>
<ScrollStatus :scroll-details="scrollDetails" direction="vertical" />
<ScrollControls
v-model:item-count="itemCount"
v-model:item-size="itemSize"
v-model:buffer-before="bufferBefore"
v-model:buffer-after="bufferAfter"
v-model:sticky-header="stickyHeader"
v-model:sticky-footer="stickyFooter"
direction="vertical"
@scroll-to-index="handleScrollToIndex"
@scroll-to-offset="handleScrollToOffset"
@refresh="virtualScrollRef?.refresh()"
/>
</template>
<VirtualScroll
ref="virtualScrollRef"
:debug="debugMode"
class="example-container table table-zebra"
:items="items"
:item-size="itemSize"
:buffer-before="bufferBefore"
:buffer-after="bufferAfter"
:sticky-header="stickyHeader"
:sticky-footer="stickyFooter"
container-tag="table"
wrapper-tag="tbody"
item-tag="tr"
aria-label="User data table"
@scroll="onScroll"
>
<template #header>
<tr class="bg-base-200 shadow-sm z-1">
<th class="w-16 text-end border-b border-base-300 py-3 text-sm small-caps tracking-widest opacity-60">ID</th>
<th class="w-48 border-b border-base-300 py-3 text-sm small-caps tracking-widest opacity-60">Name</th>
<th class="w-72 border-b border-base-300 py-3 text-sm small-caps tracking-widest opacity-60">Email</th>
<th class="w-24 text-center border-b border-base-300 py-3 text-sm small-caps tracking-widest opacity-60">Age</th>
<th class="w-56 border-b border-base-300 py-3 text-sm small-caps tracking-widest opacity-60">City</th>
<th class="w-24 text-center border-b border-base-300 py-3 text-sm small-caps tracking-widest opacity-60">Role</th>
<th class="w-24 text-center border-b border-base-300 py-3 text-sm small-caps tracking-widest opacity-60">Status</th>
</tr>
</template>
<template #item="{ item, index }">
<td class="w-16 text-end font-mono text-sm opacity-50">#{{ index }}</td>
<td class="w-48 font-bold text-sm">{{ item.name }}</td>
<td class="w-72 text-xs opacity-80">{{ item.email }}</td>
<td class="w-24 text-center text-sm tabular-nums">{{ item.age }}</td>
<td class="w-56 text-sm">{{ item.city }}</td>
<td class="w-24 text-center">
<span
class="badge badge-xs @4xl:badge-sm font-semibold"
:class="{
'badge-primary': item.role === 'Admin',
'badge-secondary': item.role === 'Editor',
'badge-soft': item.role === 'Viewer',
}"
>
{{ item.role }}
</span>
</td>
<td class="w-24 text-center">
<span
class="badge badge-xs @4xl:badge-sm font-semibold"
:class="item.status === 'Active' ? 'badge-success' : 'badge-error'"
>
{{ item.status }}
</span>
</td>
</template>
<template v-if="stickyFooter" #footer>
<tr class="bg-base-200 shadow-sm z-1">
<td class="w-full p-4 font-bold text-center border-t border-base-300 text-xs small-caps tracking-widest opacity-60" colspan="7">
End of {{ itemCount.toLocaleString() }} items
</td>
</tr>
</template>
</VirtualScroll>
</ExampleContainer>
</template>
- Scroll Status
- Directionvertical
- Current Item #-
- Rendered Range #0:0
- Total Size (px)0h
- Viewport Size (px)0h
- Scroll Offset (px)0y
- Controls