A virtual list keeps all items as data but puts only the rows near the viewport in the DOM. You give it an empty list virtual-list element, the items array, a renderItem(item) function that returns one <li>, and a row height; it does the rest as you scroll. The inventory page creates it in $onMounted:
vl = $f7.virtualList.create({
el: $el.value.find('.inventory-list'),
items: [],
renderItem,
height: $f7.theme === 'ios' ? 62 : 54, // measured row heights, in pixels
searchAll: (query, items) => { // every word must match, like the searchbar's own rule
const words = query.trim().toLowerCase().split(/\s+/);
return items.flatMap((c, i) => {
const text = `${c.title} ${c.author} ${c.condition}`.toLowerCase();
return words.every((w) => text.includes(w)) ? [i] : [];
});
},
});The heights were measured from the rendered rows (62 px in iOS, 54 px in Material with these fonts). A wrong fixed height makes rows overlap or leave gaps; pass height: 'auto' or a function per item when rows differ, which costs a measurement per row. appendItems(), replaceAllItems() and deleteItem() change the data. A searchbar whose searchContainer is the list calls searchAll instead of hiding rows, most of which are not in the DOM.