background
A recent project involved a scenario where the el-Table implementation of the Ele. me component needed to roll to the bottom to load more data.
The problem
In the element-UI documentation, there is a directive called V-infinite-Scroll, but in practice, it is found that the directive only applies to the currently bound element. As follows:
<div style="overflow:auto; height:300px" v-infinite-scroll="load">
<ul>
<li>Lao tze today</li>
<li>Don't go to work</li>
</ul>
</div>// div is the container where the scrollbar appearsCopy the code
There is nothing you can do for tags whose
is not the actual scrollbar
<el-table v-infinite-scroll="load">
<el-table-column></<el-table-column>
<el-table-column></<el-table-column>
<el-table-column></<el-table-column>
</el-table>// The el-table tag is not a real container, it is bound to the directive, it does not take effect...Copy the code
Baidu later found, really what a mess of solutions have ah (CSDN you see me why?) “And decided to do it yourself
implementation
The project adopts Vue, so the host adopts the solution of Vue to realize: Vue instruction
The first thing we need to know is how do we figure out how to roll to the bottom
const { scrollTop, scrollHeight, clientHeight } = targetEl
if (scrollHeight === scrollTop + clientHeight) {
console.log('To the bottom!! ')}Copy the code
With the key points of this implementation clear, let’s implement a simple version
Concise version
export default {
name: 'load-more',
bind (el, binding, vnode) {
binding.handler = function () {
const { scrollTop, scrollHeight, clientHeight } = el
if (scrollHeight === scrollTop + clientHeight) {
binding.value && binding.value()
}
}
el.addEventListener('scroll', binding.handler)
},
unbind (el, binding) {
el.removeEventListener('scroll', binding.handler)
el = null}}Copy the code
At this point, we register the written instructions in the component. A simple test showed that our instructions had worked
<template>
<div style="overflow:auto; height:300px" v-load-more="haha">
<ul>
<li>Lao tze today</li>
<li>Don't go to work</li>
</ul>
</div>
</template>
<script>
name: 'list'.methods: {
haha () {
console.log('It works!! ')}}</script>
Copy the code
Expansion of capabilities
To return to our original question, we hope that our directive supports this
- The element that can be specified
- Whether to disable unlimited loading
- Supports setting the offset to the bottom of the scroll
- Support image stabilization
The complete code
/ * * *@author: cunhang_wei
* @description: listens for scrolling to the bottom of the list */
const debounce = function (func, delay) {
let timer = null
return function () {
if (timer) clearTimeout(timer)
timer = null
let self = this
let args = arguments
timer = setTimeout(() = > {
func.apply(self, args)
}, delay)
}
}
export default {
name: 'load-more',
bind (el, binding, vnode) {
const { expand } = binding.modifiers
// With richer functionality, directives that support the parent are applied to the specified child component
if (expand) {
/** * target Class name of the target DOM node * distance Distance threshold to reduce the triggered loading, in units of px * func triggered method * delay Anti - buffing delay, in units of ms * load-more-disabled Whether to disable unlimited loading */
let { target, distance = 0, func, delay = 200 } = binding.value
if (typeoftarget ! = ='string') return
let targetEl = el.querySelector(target)
if(! targetEl) {console.log('Can't find container')
return
}
binding.handler = debounce(function () {
const { scrollTop, scrollHeight, clientHeight } = targetEl
let disabled = el.getAttribute('load-more-disabled')
disabled = vnode[disabled] || disabled
if (scrollHeight <= scrollTop + clientHeight + distance) {
if (disabled) return
func && func()
}
}, delay)
targetEl.addEventListener('scroll', binding.handler)
} else {
binding.handler = helper.debounce(function () {
const { scrollTop, scrollHeight, clientHeight } = el
if (scrollHeight === scrollTop + clientHeight) {
binding.value && binding.value()
}
}, 200)
el.addEventListener('scroll', binding.handler)
}
},
unbind (el, binding) {
let { arg } = binding
// With richer functionality, directives that support the parent are applied to the specified child component
if (arg === 'expand') {
/** * target Class name of the target DOM node * offset Distance threshold for triggering loading, expressed in ms * method Method for triggering loading * delay Delay for buffing, expressed in ms */
const { target } = binding.value
if (typeoftarget ! = ='string') return
let targetEl = el.querySelector(target)
targetEl && targetEl.removeEventListener('scroll', binding.handler)
targetEl = null
} else {
el.removeEventListener('scroll', binding.handler)
el = null}}}Copy the code
test
We find the real scroll container for the
tag
<template>
<div>
<el-table v-load-more.expand="{func: haha, target: '.el-table__body-wrapper', delay: 500}">
<el-table-column></<el-table-column>
<el-table-column></<el-table-column>
<el-table-column></<el-table-column>
</el-table>
</div>
</template>
<script>
name: 'list'.methods: {
haha () {
console.log('It works!! ')}... }</script>
Copy the code
Demonstration effect
CodePen Demo code address
Codepen. IO/afine970 / PE…
The last
Good good study will not be bad, I love your little ship, after reading feel good remember to click like oh