Skip to content

Instantly share code, notes, and snippets.

@thgiang
Last active June 18, 2020 06:56
Show Gist options
  • Save thgiang/91979c25c4cddeeeb395b58228744e6f to your computer and use it in GitHub Desktop.
Save thgiang/91979c25c4cddeeeb395b58228744e6f to your computer and use it in GitHub Desktop.
A small Vue component to render big array of items. It will not render invisible <tr> tags. This help increase performance.
READ AND REMOVE ME: Replace this.$store.state.blogs.data by your items array
READ AND REMOVE ME: currentScrollPosition + 1000 mean only render from current position + 1000px, you can change as you want
<template>
<table class="table table-fixed">
<thead>
<tr>
<th scope="col">Content</th>
</tr>
</thead>
<tbody :style="'min-height:'+minTableHeight+'px'">
<tr v-for="(item, index) in this.$store.state.blogs.data" :id="'item_no_'+index"
:style="'height: '+getHeightOfTr(index)+'px'">
<template v-if="itemsPosition[index] >= currentScrollPosition && itemsPosition[index] < currentScrollPosition + 1000">
<td><strong>{{item.id}}</strong> {{item.content}}</td>
</template>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: 'table-virtual-scroller',
data() {
return {
minRowHeight: 20,
minTableHeight: 0,
itemsHeight: {},
itemsPosition: {},
currentScrollPosition: 1,
}
},
methods: {
getHeightOfTr(index) {
return this.itemsHeight.hasOwnProperty(index) ? this.itemsHeight[index] : this.minRowHeight;
},
saveHeightOfTr() {
// Init
let itemsHeightCopy = this.itemsHeight;
let itemsPositionCopy = this.itemsPosition;
let lastRowYPosition = 0;
// Foreach
for (let index in this.$store.state.blogs.data) {
let elementExists = document.getElementById('item_no_' + index);
let height = elementExists ? elementExists.offsetHeight : this.minRowHeight;
lastRowYPosition += height;
itemsHeightCopy[index] = height;
itemsPositionCopy[index] = lastRowYPosition;
}
// Update state
this.minTableHeight = lastRowYPosition;
this.itemsHeight = itemsHeightCopy;
this.itemsPosition = itemsPositionCopy;
console.log(this.itemsHeight);
},
checkScrollPosition(e = {}) {
let el = this.$el;
// Only catch my scroll, avoid parent 's scroller
if (
(el.scrollTop === 0 && e.deltaY < 0) ||
(Math.abs(el.scrollTop - (el.scrollHeight - el.clientHeight)) <= 1 &&
e.deltaY > 0)
) {
e.preventDefault();
}
//
this.updateWindow(e);
},
updateWindow(e) {
this.currentScrollPosition = this.$el.scrollTop;
}
},
updated() {
// Re-calculate height of all tr
this.saveHeightOfTr();
},
mounted() {
// Listen for scroll event
window.vscroll = this;
this._checkScrollPosition = this.checkScrollPosition.bind(this);
this.checkScrollPosition();
this.$el.addEventListener("scroll", this._checkScrollPosition);
this.$el.addEventListener("wheel", this._checkScrollPosition);
},
beforeDestroy() {
this.$el.removeEventListener("scroll", this._checkScrollPosition);
this.$el.removeEventListener("wheel", this._checkScrollPosition);
},
}
</script>
<style scoped>
table {
display: block;
height: 80vh !important;
overflow-y: auto;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment