Virtual List

Virtual List allows to render lists with huge amount of elements without loss of performance. And it is fully compatible with all Framework7 components which work with lists such as Search Bar, Infinite Scroll, Pull To Refresh, Swipeouts (swipe-to-delete) and Sortable.

Virtual List HTML Layout

Virtual List HTML layout is pretty simple, it is almost the same as for usual List View or Media List View with only difference, you need to leave it empty:

<!-- Virtual List -->
<div class="list-block virtual-list">
  <!-- keep it empty -->
</div>

Where:

  • virtual-list - required additional class on any list-block that uses Virtual List

Initialize Virtual List

Now, when we have list's HTML, we need to initialize it. We need to use required App method:

myApp.virtualList(listBlockContainer, parameters) - initialize virtual list with parameters

  • listBlockContainer - HTMLElement or string (with CSS Selector) of "list-block" HTML element. Required.
  • parameters - object - object with virtual list parameters. Required.
  • Method returns initialized Virtual List instance

For example:

var myList = app.virtualList('.list-block', {
    items: [1,2,3,4],
    height: 44
});   
Note that Virtual List container (listBlockContainer) should be in DOM on a moment of initialization. So if you use virtual list not on home page, you need to initialize it within page:init (or page:beforeinit) event

Virtual List Parameters

Let's look on list of all available parameters:

Parameter Type Default Description
items array Array with list items
rowsBefore number Amount of rows (items) to be rendered before current screen scroll position. By default it is equal to double amount of rows (items) that fit to screen
rowsAfter number Amount of rows (items) to be rendered after current screen scroll position. By default it is equal to the amount of rows (items) that fit to screen
cols number 1 Number of items per row. Doesn't compatible when using Virtual List with dynamic height
height number or function(item) 44 If number - item height in px. If function then function should return item height
template string or function Template7 string template or Template7 compiled template that used to render single item. Template should contain full HTML layout for single item, including wrapping <li></li> tags
renderItem function(index, item) This optional function allows to use custom function to render item HTML. It could be used instead of template parameter
renderExternal function(vlInstance, renderParameters) This optional function allows to render DOM items using some custom method. Useful in case it is used (e.g.) with Vue/React plugin to pass DOM rendering and manipulation to Vue/React. renderParameters conaints object with the following properties: fromIndex, toIndex, listHeight, topPosition, items
emptyTemplate string Defines list item template for the case if empty data passed
dynamicHeightBufferSize number 1 This parameter allows to control buffer size on Virtual Lists with dynamic height (when height parameter is function) as a buffer size multiplier
cache boolean true Disable or enable DOM cache for already rendered list items. In this case each item will be rendered only once and all futher manipulations will be with DOM element. It is useful if your list items have some user interaction elements (like form elements or swipe outs) or could be modified
updatableScroll boolean Is the current device updates and handles scroll events during scroll. By default (if not specified) it is "false" for all iOS devices with iOS version less than 8.
showFilteredItemsOnly boolean false Option to show filtered items only set by `filter()` method
Search
searchByItem function(query, index, item) Search function that will be used by Search Bar, it receives search query, item index and item itself. If item matches to search query you need to return true, otherwise this function should return false
searchAll function(query, items) Search function that will be used by Search Bar, it receives search query and array with all items. You need to loop through items and return array with indexes of matched items
Callbacks
onItemBeforeInsert function(list, item) Callback function, will be executed before item will be added to virtual document fragment
onBeforeClear function(list, fragment) Callback function, will be executed before current DOM list will be removed and replaced with new document fragment
onItemsBeforeInsert function(list, fragment) Callback function, will be executed after current DOM list will be removed and before new document will be inserted
onItemsAfterInsert function(list, fragment) Callback function, will be executed after new document fragment with items inserted

Virtual List Methods & Properties

After we initialize Virtual List we have its initialized instance in variable (like myList variable in example above) with helpful methods and properties:

Properties
myList.items Array with items
myList.filteredItems Array with filtered items (after using ".filterItems" method)
myList.domCache Object with cached dom items
myList.params Parameters passed on list initialization
myList.listBlock Dom7 instance of virtual list "list-block" container
myList.pageContent Dom7 instance of parent "page-content" container
myList.currentFromIndex Index number of currently first rendered item
myList.currentToIndex Index number of currently last rendered item
myList.reachEnd Boolean property. Equals true if the currently last rendered item is the last item of all specified items
Methods
myList.filterItems(indexes); Filter virtual list by passing array with indexes of items to show
myList.resetFilter(); Disable filter and display all items again
myList.appendItem(item); Append item to virtual list
myList.appendItems(items); Append array with items to virtual list
myList.prependItem(item); Prepend item to virtual list
myList.prependItems(items); Prepend array with items to virtual list
myList.replaceItem(index, items); Replace item at specified index with the new one
myList.replaceAllItems(items); Replace all items with arrays of new items
myList.moveItem(oldIndex, newIndex); Move virtual item from oldIndex to newIndex
myList.insertItemBefore(index, item); Insert new item before item with specified index
myList.deleteItem(index); Delete item at specified index
myList.deleteItems(indexes); Delete items at specified array of indexes
myList.deleteAllItems(); Delete all items
myList.clearCache(); Clear virtual list cached DOM elements
myList.destroy(); Destory initialized virtual list and detach all events
myList.update(); Update virtual list, including recalculation of list sizes and re-rendering of virtual list
myList.scrollToItem(index); Scroll Virtual List to specified item by its index number

Examples

Plain Items

In the most simple case when we don't need to use templating for items (without template and renderItem parameters) and when we don't need much logic here, we may just pass items HTML layout in items parameter:

var myList = myApp.virtualList('.list-block.virtual-list', {
    // Array with plain HTML items
    items: [
        '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 1</div></div></li>',
        '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 2</div></div></li>',
        '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 3</div></div></li>',
        //...
        '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 1000</div></div></li>'
    ]
});

Templating

But in most cases we may need some logic that we can use for filtering and templating, or if we load items from some JSON data. In this case we may pass array with data objects in items parameter and use Template7 template parameter or custom render function using renderItem parameter:

With Template7 template:

var myList = myApp.virtualList('.list-block.virtual-list', {
    // Array with items data
    items: [
        {
            title: 'Item 1',
            picture: 'path/to/picture1.jpg'
        },
        {
            title: 'Item 2',
            picture: 'path/to/picture2.jpg'
        },
        // ...
        {
            title: 'Item 1000',
            picture: 'path/to/picture1000.jpg'
        },
    ],
    // Template 7 template to render each item
    template: '<li class="item-content">' +
                  '<div class="item-media"><img src="{{picture}}"></div>' +
                  '<div class="item-inner">' +
                      '<div class="item-title">{{title}}</div>' +
                  '</div>' +
               '</li>'
});            

With custom render function:

var myList = myApp.virtualList('.list-block.virtual-list', {
    // Array with items data
    items: [
        {
            title: 'Item 1',
            picture: 'path/to/picture1.jpg'
        },
        {
            title: 'Item 2',
            picture: 'path/to/picture2.jpg'
        },
        // ...
        {
            title: 'Item 1000',
            picture: 'path/to/picture1000.jpg'
        },
    ],
    // Custom render function to render item's HTML
    renderItem: function (index, item) {
        return '<li class="item-content">' +
                  '<div class="item-media"><img src="' + item.picture + '"></div>' +
                  '<div class="item-inner">' +
                      '<div class="item-title">' + item.title + '</div>' +
                  '</div>' +
               '</li>';
    }
});            

Using With Search Bar

If we use Virtual List with search bar we need to provide search function in parameters using searchAll or searchByItem parameters:

var myList = myApp.virtualList('.list-block.virtual-list', {
    // Array with items data
    items: [
        {
            title: 'Item 1',
            picture: 'path/to/picture1.jpg'
        },
        {
            title: 'Item 2',
            picture: 'path/to/picture2.jpg'
        },
        // ...
        {
            title: 'Item 1000',
            picture: 'path/to/picture1000.jpg'
        },
    ],
    // search all items, we need to return array with indexes of matched items
    searchAll: function (query, items) {
        var foundItems = [];
        for (var i = 0; i < items.length; i++) {
            // Check if title contains query string
            if (items[i].title.indexOf(query.trim()) >= 0) foundItems.push(i);
        }
        // Return array with indexes of matched items
        return foundItems; 
    }
});  

The same but with searchByItem parameter:

var myList = myApp.virtualList('.list-block.virtual-list', {
    // Array with items data
    items: [
        {
            title: 'Item 1',
            picture: 'path/to/picture1.jpg'
        },
        {
            title: 'Item 2',
            picture: 'path/to/picture2.jpg'
        },
        // ...
        {
            title: 'Item 1000',
            picture: 'path/to/picture1000.jpg'
        },
    ],
    // search item by item
    searchByItem: function (query, index, item) {
        // Check if title contains query string
        if (item.title.indexOf(query.trim()) >= 0) {
            return true; //item matches query
        }
        else {
            return false; //item doesn't match
        }
    }
}); 

Dynamic Height

If not all of our items should be the same height we use dynamic height by specifying function in height parameter instead of number:

var myList = myApp.virtualList('.list-block.virtual-list', {
    // Array with items data
    items: [
        {
            title: 'Item 1',
            picture: 'path/to/picture1.jpg'
        },
        {
            title: 'Item 2'
        },
        {
            title: 'Item 3',
            picture: 'path/to/picture3.jpg'
        },
        // ...
        {
            title: 'Item 1000'
        },
    ],
    // Item template
    template: '...',
 
    // Height function
    height: function (item) {
        if (item.picture) return 100; //item with picture is 100px height
        else return 44; //item without picture is 44px height
    }
});     

Note that script will not actually set height on your items, so make sure that your items are really have specified height. You may also need to set it in CSS or using "style" attribute in template or renderItem parameters

Also note that dynamic height currently is not compatible with cols parameter

API Methods

If we need to add, remove, replace or move items in list we need to use Virtual List methods:

// Initialize List
var myList = myApp.virtualList('.list-block.virtual-list', {
    // Array with items data
    items: [
        {
            title: 'Item 1'
        },
        {
            title: 'Item 2'
        },
        // ...
        {
            title: 'Item 1000'
        }
    ],
 
    // Item template
    template: '...',
});
 
// Append Item
myList.appendItem({
    title: 'Item 1001'
});
 
// Append multiple items when clicking on some button
$('.button.append-items').on('click', function () {
    // Append multiple items by passing array with items
    myList.appendItem([
        {
            title: 'Item 1002'
        },
        {
            title: 'Item 1003'
        },
        {
            title: 'Item 1004'
        }
    ]);    
});
 
// Replace First Item
myList.replaceItem(0, {
    title: 'New Item 1'
});
 
// Show only first 10 items when clicking on button
$('.button.show-first-10').on('click', function () {
    // We need to pass array with indexes of items to show
    myList.filter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
});
 
// Reset filter
$('.button.reset-filter').on('click', function () {
    myList.resetFilter();
});
 
// Insert new item before 1st and 2nd:
myList.insertItemBefore(1, {
    title: 'Item 1.5'
});    

Note that when you use methods to manipulate virtual list items you need to pass each item in the same format as you specify them in items parameter