Element组件 el-select设置滚动条+滚动加载(两种写法: 原生Js和自定义指令)

系列文章目录


提示:下面是简单的功能实现(时间紧迫,大晚上赶工)


背景与展望

这次的功能甚至可以说一点也不复杂,但是作为一名前端开发.感觉这种交互行为其实出现在我们这个及其简易的功能场景中,就很离谱。需求做了又砍掉,纯纯的浪费时间,唉

本次的功能主要如下:

  1. 给el-select添加滚动条(设置样式)
  2. 给el-select的滚动条注册scroll事件(记得一定要移除scroll
  3. 体验感优化方法如下:
    添加节流或防抖功能
    添加loading交互
  4. 场景优化:
    不要污染全局的select框
    缓存选择的options(回显)
    采用footer插槽改变现有交互
    拓展远端搜索供能
    usehooks的提取(后面更新)
    自定义指令的的声明(后面更新)

一、使用原生的js实现触底加载

1.效果图如下:

请添加图片描述

2.HTML如下

// 此处使用的是高版本的 el-Select(具体的版本使用的方法都是类似的,下面会提到)
<body>
    <div id="app">
        <div style="margin: 50px 0px;"><el-text class="mx-1" type="primary">初始化的options数据条数如下:{{optionsLength}}</el-text></div>
        <el-select multiple   v-model="value" placeholder="Select" style="width: 160px;    height: 180px;" @visible-change="handleScroll">
            <el-option
                v-for="item in options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            >
            </el-option>
        </el-select>

    </div>
</body>

3.JS代码如下

//因为是使用的HTML文档 所以此处的代码风格是Vue2的
//(我上手还是蛮快的,哈哈哈哈哈哈,都3年多不写Vue2的代码了)
<script>
    const mockData1=[{label:"ceshi ",value:"sele1ct1"},{label:"ce2shi ",value:"sele12ct1"},{label:"cesh2i ",value:"sele3ct1"},{label:"ceshi3 ",value:"select1"},{label:"ceshi4 ",value:"sel1ec23t1"}]
    const { ElMessage } = ElementPlus;
    const App = {
        data() {
            return {
                message: "Hello Element Plus",
                options:[],
                value:"",
                mockOptions:[]
            };
        },
        computed:{
            optionsLength:function(){
                return this.options.length
            }
        },
        methods: {
            createNewOptionsData:function(){
                const str="测试dflklsadj我f的k京东数科了解阿ghjghjgj卡丽打飞gjghj机考拉激发lajfdkl"
                const randomNumber = Math.floor(Math.random()*(str.length-2))
                const result =str.split("")[randomNumber] + str.split("")[randomNumber+1]
                this.options.push({label:"随机生成:"+result,value:result})
            },
            scrollEvent(event){
                const domTarget =event.target
                const translateY = domTarget.scrollTop
                const viewheight =domTarget.clientHeight
                const allHeight = domTarget.scrollHeight
                if(allHeight-translateY<=viewheight){
                    this.createNewOptionsData()
                    // 添加一个数据
                    ElMessage({
                        message: `在Options中添加了一条数据,当前的条数为${this.options.length}`,
                        type: 'success',
                    })
 
                }
            }
            ,
            handleScroll:function(val){
            //这里注释的三个类,主要是抓的弹窗中的popover类,
            //一般情况下都可以使用,我这里使用的是 [el-scrollbar__wrap]
                // el-select-dropdown__wrap el-scrollbar__wrap el-scrollbar__wrap--hidden-default
                const dom = document.querySelector(".el-scrollbar__wrap")
                if(!val){
                    dom.removeEventListener("scroll",this.scrollEvent)
                }
               
                dom.addEventListener("scroll",this.scrollEvent)

            }
        },
        mounted() {
           	//此处都是为了mock数据
            this.options =mockData1
            this.createNewOptionsData()
        },
    };
    const app = Vue.createApp(App);
    app.mount("#app");
</script>



二、使用自定义指令解决问题(已补全-2024-05-19)

敬请期待(2024-05-16 23:55)

1.效果图如下:

请添加图片描述

2.新增功能如下:

  1. JavaScript原生写法 ===> 自定义指令触发
  2. 给触发事件添加节流效果
  3. 将事件缓存到节点上

3.后续可以优化功能如下:

  1. 拓展响应式数据的池子(支持更多功能)
  2. 拓展使用场景。不单单作用el-select组件
  3. 拓展方法的配置

4.HTML代码如下:

// 结构如下
<body>
    <div id="app">
        <div style="margin: 50px 0px;">
            <el-text class="mx-1" type="primary">初始化的options数据条数如下:{{optionsLength}}</el-text>
        </div>
        <el-select v-if="boolen" v-selectscroll="options" multiple popper-class="select-scroll" v-model="value"
            placeholder="Select" style="width: 160px; height: 180px;">
            <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
            </el-option>
        </el-select>

    </div>
</body>

4.Js代码如下:

    const { ElMessage } = ElementPlus;
    const { createApp, ref, watchEffect, watch } = Vue;
    const mockData1 = [{ label: "ceshi ", value: "sele1ct1" }, { label: "ce2shi ", value: "sele12ct1" }, { label: "cesh2i ", value: "sele3ct1" }, { label: "ceshi3 ", value: "select1" }, { label: "ceshi4 ", value: "sel1ec23t1" }]
    const App = {
        data() {
            return {
                options: [],
                value: "",
            };
        },
        computed: {
            optionsLength: function () {
                return this.options.length
            }
        },
    };
    const app = createApp(App);
    app.directive("selectscroll", {
        created: function (el, binding) {
            el.__flag = false
            const getCurrentTimer = () => {
                const now = new Date();
                const year = now.getFullYear();
                const month = (now.getMonth() + 1).toString().padStart(2, '0');
                const day = now.getDate().toString().padStart(2, '0');
                const hours = now.getHours().toString().padStart(2, '0');
                const minutes = now.getMinutes().toString().padStart(2, '0');
                const seconds = now.getSeconds().toString().padStart(2, '0');
                const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
                return formattedDate
            }
            el.__curentTimer = getCurrentTimer
            console.log("自定义指令created");
        },
        mounted: (el, binding) => {
            console.log("自定义指令mounted");
            mockData1.forEach(item => {
                binding.value.push(item)
            })

            const createNewOptionsData = async function () {
                const str = "测试dflklsadj我f的k京东数科了解阿ghjghjgj卡丽打飞gjghj机考拉激发lajfdkl"
                const randomNumber = Math.floor(Math.random() * (str.length - 2))
                const result = str.split("")[randomNumber] + str.split("")[randomNumber + 1]
                binding.value.push({ label: "随机生成:" + result, value: result })
                const node = document.createElement('p')
                node.innerText = `生成数据的时间是:${el.__curentTimer()}`
                document.body.appendChild(node)
            }
            const handleScroll = async function (event) {

                clearTimeout(el.__flag)
                el.__flag = setTimeout(async () => {
                    const domTarget = event.target
                    const translateY = domTarget.scrollTop
                    const viewheight = domTarget.clientHeight
                    const allHeight = domTarget.scrollHeight
                    if (allHeight - translateY <= viewheight + 2) {

                        await createNewOptionsData()
                        // 添加一个数据
                        ElMessage({
                            message: `在Options中添加了一条数据,当前的条数为${binding.value.length}`,
                            type: 'success',
                        })

                    }
                }, 1500);



            }
            const dom = document.querySelector(".select-scroll .el-scrollbar__wrap")
            dom.addEventListener("scroll", handleScroll)
            el.__handleScroll = handleScroll
            el.__scrollDom = dom

        },
        unmounted(el, binding) {
            // const dom = document.querySelector(".select-scroll .el-scrollbar__wrap")
            el.__scrollDom.removeEventListener("scroll", el.__handleScroll)
            console.log("成功注销了");
        }

    })


总结

希望自己的写法可以帮助到遇到问题的人。有不足之处,或者可以优化的地方,欢迎大家给出自己的建议,会从大家的建议中汲取营养,提升自己!!!

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

满脑子技术的前端工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值