Vue JavaScript
Latest commit eb5c74b Jul 13, 2017 @Akryum Bump version

README.md

vue-virtual-scroller

npm npm vue2

Smooth scroll with any amount of data (demo).

Table of contents

Installation

npm install --save vue-virtual-scroller

⚠️ vue-virtual-scroller now uses vue-observe-visibility to automatically refresh itself when shown to prevent display glitches. This means you need to include the polyfills needed by vue-observe-visibility for this to work.

Default import

Install all the components:

import Vue from 'vue'
import VueVirtualScroller from 'vue-virtual-scroller'

Vue.use(VueVirtualScroller)

Use specific components:

import Vue from 'vue'
import { VirtualScroller } from 'vue-virtual-scroller'

Vue.component('virtual-scroller', VirtualScroller)

⚠️ A css file is included when importing the package. You may have to setup your bundler to embed the css in your page.

Distribution import

Install all the components:

import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import VueVirtualScroller from 'vue-virtual-scroller/dist/vue-virtual-scroller'

Vue.use(VueVirtualScroller)

Use specific components:

import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import { VirtualScroller } from 'vue-virtual-scroller/dist/vue-virtual-scroller'

Vue.component('virtual-scroller', VirtualScroller)

⚠️ You may have to setup your bundler to embed the css file in your page.

Browser

<link rel="stylesheet" href="vue-virtual-scroller/dist/vue-virtual-scroller.css"/>

<script src="vue.js"></script>
<script src="vue-virtual-scroller/dist/vue-virtual-scroller.js"></script>

Install all the components:

Vue.use(VueVirtualScroller)

Use specific components:

Vue.component('virtual-scroller', VueVirtualScroller.VirtualScroller)

Source import

Install all the components:

import Vue from 'vue'
import VueVirtualScroller from 'vue-virtual-scroller/src'

Vue.use(VueVirtualScroller)

Use specific components:

import Vue from 'vue'
import { VirtualScroller } from 'vue-virtual-scroller/src'

Vue.component('virtual-scroller', VirtualScroller)

⚠️ You need to configure your bundler to compile .vue files. More info in the official documentation.

Usage

The virtual scroller has three main props:

  • items is the list of items you want to display in the scroller. There can be several types of item.
  • itemHeight is the display height of the items in pixels used to calculate the scroll height and position. If it set to null (default value), it will use variable height mode.
  • renderers is a map of component definitions objects or names for each item type (more details). If you don't define renderers, the scroller will use scoped slots (see below).

You need to set the size of the virtual-scroller element and the items elements (for example, with CSS). Unless you are using variable height mode, all items should have the same height to prevent display glitches.

The browsers have a height limitation on DOM elements, it means that currently the virtual scroller can't display more than ~500k items depending on the browser.

Renderers

The optional renderers prop is an object containing a component definition for each possible value of the item type. If you don't set this prop, scoped slots will be used instead. The component definition must have an item prop, that will get the item object to render in the scroller. It will also receive an index prop.

There are additional props you can use:

  • typeField to customize which field is used on the items to get their type and use the corresponding definition in the renderers map. The default is 'type'.
  • keyField to customize which field is used on the items to set their key special attribute (see the documation). The default is 'id'.

For better performance, you should use the keyField prop that will set the key attribute. Warning! You shouldn't expect items to have the key set at all times, since the scroller may disable them depending on the situation.

Scoped slots

Alternatively, you can use scoped slots instead of renderers. This is active when you don't define the renderers prop on the virtual scroller.

The scope will contain the row's item in the item attribute, so you can write scope="props" and then use props.item. It will also have an index attribute.

Here is an example:

<virtual-scroller class="scroller" :items="items" item-height="42" content-tag="table">
  <template scope="props">
    <tr v-if="props.item.type === 'letter'" class="letter" :key="props.itemKey">
      <td>
        {{props.item.value}} Scoped
      </td>
    </tr>

    <tr v-if="props.item.type === 'person'" class="person" :key="props.itemKey">
      <td>
        {{props.item.value.name}}
      </td>
    </tr>
  </template>
</virtual-scroller>

For better performance, you should set the key attribute on direct children using the itemKey field from the scoped slot and set the keyField prop on the virtual scroller.

Page mode

The page mode expand the virtual-scroller and use the page viewport to compute which items are visible. That way, you can use it in a big page with HTML elements before or after (like a header and a footer). Just set the page-mode props to true:

<header>
  <menu></menu>
</header>

<virtual-scroller page-mode></virtual-scroller>

<footer>
  Copyright 2017 - Cat
</footer>

Variable height mode

⚠️ This mode can be performance heavy with a lot of items. Use with caution.

If the itemHeight prop is not set or set to null, the virtual scroller will switch to Variable height mode. You then need to expose a number field on the item objects with the height of the item element.

⚠️ You still need to set the height of the items with CSS correctly (with classes for example).

Use the heightField prop (default is 'height') to set the field used by the scroller to get the height for each item.

Example:

const items = [
  {
    id: 1,
    label: 'Title',
    height: 64,
  },
  {
    id: 2,
    label: 'Foo',
    height: 32,
  },
  {
    id: 3,
    label: 'Bar',
    height: 32,
  },
]

Customizing the tags

These are optional props you can use to change the DOM tags used in the virtual scroller:

  • mainTag to change the DOM tag of the component root element. The default is 'div'.
  • containerTag to change the DOM tag of the element simulating the height. The default is 'div'.
  • contentTag to change the DOM tag of the element containing the items. The default is 'div'. For example, you can change this to 'table'.

The component template is structured like this:

<main>
  <container>
    <content>
      <!-- Your items here -->
    </content>
  </container>
</main>

If you set contentTag to 'table', the actual result in the DOM will look like the following:

<div>
  <div>
    <table>
      <!-- Your items here -->
    </table>
  </div>
</div>

Customizing the classes

You can use the following props to customize the container and content elements CSS classes:

  • containerClass
  • contentClass

Slots

There are 4 slots you can use to inject things inside the scroller (it may be usefull to add a thead or tbody):

<main>
  <slot name="before-container"></slot>
  <container>
    <slot name="before-content"></slot>
    <content>
      <!-- Your items here -->
    </content>
    <slot name="after-content"></slot>
  </container>
  <slot name="after-container"></slot>
</main>

Example

<template>
  <div class="demo">
    <virtual-scroller
      class="scroller"
      :items="items"
      :renderers="renderers"
      item-height="22"
      type-field="type">
    </virtual-scroller>
  </div>
</template>

<script>
// Data with a type field
const items = [
  { type: 'letter', value: 'A' },
  { type: 'person', value: { name: 'Alan' } },
  { type: 'person', value: { name: 'Alice' } },
]

import Letter from './Letter.vue'
import Item from './Item.vue'

// Bind the components to the item type
const renderers = Object.freeze({
  letter: Letter,
  person: Item,
})

export default {
  data: () => ({
    items,
    renderers,
  }),
}
</script>

<style>
.scroller {
  height: 100%;
}

.scroller .item {
  height: 22px;
}
</style>

Letter.vue source:

<template>
  <div class="letter">({{item.index}}) {{item.value}}</div>
</template>

<script>
export default {
  props: ['item'],
}
</script>

Item.vue source:

<template>
  <div class="person" @click="edit">({{item.index}}) {{item.value.name}}</div>
</template>

<script>
export default {
  props: ['item'],
  methods: {
    edit () {
      this.item.value.name += '*'
    },
  },
}
</script>

License

MIT