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

  1. The element that can be specified
  2. Whether to disable unlimited loading
  3. Supports setting the offset to the bottom of the scroll
  4. 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