语言: 英文中文

虚拟列表

虚拟列表可以用来渲染有大量数据的列表,并且不有有任何的性能问题。并且,它和所有的Framework7组件都是兼容的,包括搜索栏、无限滚动、下拉刷新、滑动删除和可排序列表。

虚拟列表的HTML结构

虚拟列表打HTML结构非常简单,他和常见的 列表 或者 多媒体列表只有一个区别,你必须把它的内容设置为空的。

<!-- Virtual List -->
<div class="list-block virtual-list">
  <!-- 内容置空 -->
</div>

其中:

  • virtual-list - 在使用虚拟列表的容器上增加这个类

初始化虚拟列表

现在,当我们有了列表的HTML,我们下一步是初始化它。我们需要用到这些App方法:

myApp.virtualList(listBlockContainer, parameters) - 使用parameters参数初始化虚拟列表

  • listBlockContainer - HTMLElement or string (with CSS Selector) 虚拟列表的容器. 必填.
  • parameters - object - 初始化参数,必填.
  • 这个方法会返回一个虚拟列表的实例

比如:

var myList = app.virtualList('.list-block', {
    items: [1,2,3,4],
    height: 44
});   
注意,在初始化的时候虚拟列表的容器必须已经在DOM中。所以,如果你的虚拟列表不是用在首页,那么你必须在 pageInit (或者 pageBeforeInit) 事件中初始化。

虚拟列表的初始化参数

下面是所有的初始化参数:

参数 类型 默认值 说明
items array 包含列表条目的数组
rowsBefore number 在当前屏幕滚动位置之前需要渲染的条目数。默认是两倍当前屏幕大小需要的数量。(译者注:也就是说,假设你的屏幕是320px高,每一个条目是40px,那么当前屏幕上方会有(320/40*2) == 16条),加上当前屏幕中的8条和屏幕下方的8条,总共在DOM中存在32个条目)。
rowsAfter number 在当前屏幕滚动位置之后需要渲染的条目数。默认是当前屏幕大小需要的数量。(译者注:参加上面的说明)。
cols number 1 每一行需要的条目数。如果你用了动态的高度,这个参数是不兼容的。
height number or function(item) 44 如果是 数字,那么它是每一个条目的px高度。如果是 函数,那么它应该返回条目的高度。
template string or function Template7 字符串模板,或者是Template7 编译后的模板,用来渲染单个条目。这个模板应该有一个条目的完整HTML结构,包括外面的 <li></li> 容器。
renderItem function(index, item) 这个可选方法用来自定义渲染条目的HTML。它是用来代替 tamplate 参数的。
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 启用/禁用已经渲染的条目的DOM缓存。启用的情况下,每一个条目都只会被渲染一次,后续都是对DOM的操作。如果你的条目中需要保存一些用户的操作(比如表单或者滑动删除)或者修改,那么这个参数是很有用的。
updatableScroll boolean 当前是否在滚动的时候会更新滚动条并触发滚动事件。默认情况下对所有iOS 8 以下的设备都是false。
搜索
searchByItem function(query, index, item) 搜索栏用的搜索方法,它接收三个参数:搜索词,条目的序号和条目自身。如果一个条目符合结果,你应该返回 true,否则返回false。
searchAll function(query, items) 搜索栏用到的搜索方法,它接受两个参数:搜索词,和包含所有条目的数组。你需要自己遍历并返回匹配的条目。
回调
onItemBeforeInsert function(list, item) 回调函数,当条目被插入到虚拟列表的 document fragment 之前执行。
onBeforeClear function(list, fragment) 回调函数,当前列表的DOM元素被删除并替换成新的 document fragment 之前执行。
onItemsBeforeInsert function(list, fragment) 回调函数,当前列表的DOM元素被删除之后,准备替换成新的 document fragment 之前执行。
onItemsAfterInsert function(list, fragment) 回调函数,新的 document fragment 插入之后执行。

虚拟列表方法和属性

当我们初始化完成之后,我们就有一个存储了虚拟列表实例的变量(比如上面例子中的 myList变量),这个实例有很有有用的方法和属性:

属性
myList.items 包含所有条目的数组
myList.filteredItems 所有过滤之后的条目(在执行了 ".filterItems" 方法之后)
myList.domCache 条目的DOM缓存
myList.params 初始化时传递的参数
myList.listBlock 虚拟列表的容器的 Dom7 实例
myList.pageContent 当前页面的 "page-content" 容器的 Dom7 实例
myList.currentFromIndex 当前被渲染的第一个条目的序号
myList.currentToIndex 当前被渲染的最后一个条目的序号
myList.reachEnd 布尔属性,如果当前的最后条目是否是所有条目中的最后一个。
方法
myList.filterItems(indexes); 过滤当前列表,只显示传入的序号指定的条目。
myList.resetFilter(); 关闭过滤,并再次显示所有的条目
myList.appendItem(item); 增加条目
myList.appendItems(items); 增加多个条目(数组)
myList.prependItem(item); 在前面增加条目
myList.prependItems(items); 在前面增加多个条目(数组)
myList.replaceItem(index, items); 把指定序号的条目换成一个新的条目
myList.replaceAllItems(items); 把所有的条目都换成新的
myList.moveItem(oldIndex, newIndex); 把一个条目从 oldIndex 换到 newIndex
myList.insertItemBefore(index, item); 在指定的序号前插入一个条目
myList.deleteItem(index); 删除指定序号的条目
myList.deleteItems(indexes); 删除指定的序号数组的条目
myList.deleteAllItems(); 删除所有条目
myList.clearCache(); 删除DOM缓存
myList.destroy(); 销毁实例,解除所有事件绑定
myList.update(); 更新列表。会重新计算列表的大小,并重新渲染
myList.scrollToItem(index); 滚动到指定位置

示例

简单的条目

最简单的一种情况,我们不需要用模板来渲染条目(没有 templaterenderItem 参数),并且我们也不需要复杂的逻辑,我们直接在 items 参数中传入了HTML字符串:

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>'
    ]
});

使用模板

但是大部分情况下,我们需要有一些过滤和渲染的逻辑,或者我们会从一个JSON字符串中加载数据。这种情况下,我们在 items 参数中传入数据,然后使用 Template7 template 参数或者使用 renderItem 自定义一个渲染函数。

使用Template7模板:

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>'
});            

使用自定义的渲染函数:

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>';
    }
});            

使用搜索栏

如果我们想使用搜索栏,我们必须在初始化的事后传入一个 searchAll 或者 searchByItem 参数:

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; 
    }
});  

和上面功能相同,但是使用 searchByItem 参数:

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
        }
    }
}); 

动态高度

如果我们的条目并不是都一样高的,那么我们可以通过 height 参数传入一个函数而不是数字来制定高度:

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
    }
});     

注意,JS并不是直接设定了你的条目的高度,所以请确保你的条目确实有指定的高度。你可能依然需要在CSS中设置,或者在 template 或者 renderItem 参数中通过 设置"style"属性来设置高度。

注意动态高度和 cols 参数是不兼容的!

API 方法

如果我们需要增加、删除、替换或者移动条目,我们需要使用虚拟列表的相关方法:

// 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'
});    

注意,如果你使用上面这些方法来操作虚拟列表,你传入的每一个条目的格式必须和你在 items 参数中指定的是一样的。