实现el-select下拉框,下拉时加载数据

发布于:2025-07-20 ⋅ 阅读:(14) ⋅ 点赞:(0)

指令

定义指令部分

import { nextTick } from 'vue';
import { debounce } from "lodash-es";

export default {
    mounted(el, binding) {
        el._binding = binding;

        // 监听点击事件
        const handleClick = async () => {
            await nextTick();
            const dropdownEl = el.querySelector('.el-select-dropdown__wrap');
            if (dropdownEl) {
                // 监听滚动事件
                setupScrollListener(dropdownEl, binding);
            }
        };

        // 监听 focus 事件(用户可能通过 Tab 键触发下拉菜单)
        const handleFocus = async () => {
            await nextTick();
            const dropdownEl = el.querySelector('.el-select-dropdown__wrap');
            if (dropdownEl) {
                // 监听滚动事件
                setupScrollListener(dropdownEl, binding);
            }
        };

        // 绑定事件
        el.addEventListener('click', handleClick);
        el.addEventListener('focus', handleFocus);

        // 保存事件处理函数,便于后续删除
        el._handleClick = handleClick;
        el._handleFocus = handleFocus;
    },

    beforeUnmount(el) {
        // 移除监听事件
        if (el._handleClick) {
            el.removeEventListener('click', el._handleClick);
            delete el._handleClick;
        }
        if (el._handleFocus) {
            el.removeEventListener('focus', el._handleFocus);
            delete el._handleFocus;
        }

        // 移除滚动监听器
        const dropdownEl = el.querySelector('.el-select-dropdown__wrap');
        if (dropdownEl && dropdownEl._handleScroll) {
            dropdownEl.removeEventListener('scroll', dropdownEl._handleScroll);
            delete dropdownEl._handleScroll;
        }
    }
};

function setupScrollListener(dropdownEl, binding) {
    // 清除之前的监听器
    if (dropdownEl._handleScroll) {
        dropdownEl.removeEventListener('scroll', dropdownEl._handleScroll);
        delete dropdownEl._handleScroll;
    }

    // 防抖
    const handleScroll = debounce(async () => {
        // 判断是否滚动到底部,滚动到底部时调用函数
        if (dropdownEl.scrollTop + dropdownEl.clientHeight >= dropdownEl.scrollHeight - 50) {
            if (typeof binding.value === 'function') {
                await binding.value();
            }
        }
    }, 200);

    // 保存
    dropdownEl._handleScroll = handleScroll;

    // 监听滚动事件
    dropdownEl.addEventListener('scroll', handleScroll);
}

注册和使用

在main中注册

app.directive('selectScroll', selectScroll)

<el-select  v-selectScroll="handleSelectScroll">
   <el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
---------------------------------------------------
const handleSelectScroll = () => {
  // console.log('触底了')
  if (list.value.length < total.value) {
    query.pageNo = query.pageNo + 1
    getList() 
  }
}

滚动事件例子

滚动事件的参数解析

  • scrollTop: 滚动到顶部的距离
  • clientHeight:可视区域的高度(包括padding,不包括border,margin,滚动条等内容)
  • scrollHeight:元素内容的总高度(包括不可见部分,如被滚动隐藏的内容),常用于判断是否滚动到底部。
  • offsetHeight:元素的总高度(包括 padding、border、margin 和滚动条)
    scrollTop + clientHeight <= scrollHeight
  • 滚动到底部:当 scrollTop + clientHeight >= scrollHeight 时,表示用户已滚动到底部。
  • 滚动到顶部:当 scrollTop === 0 时,表示用户滚动到顶部。

应用例子

实现无限滚动

window.addEventListener("scroll", () => {
  if (isAtBottom()) {
    loadMoreData(); // 加载更多数据
  }
});

function isAtBottom() {
  const scrollTop = window.scrollY || document.documentElement.scrollTop;
  const clientHeight = window.innerHeight;
  const scrollHeight = document.documentElement.scrollHeight;
  return scrollTop + clientHeight >= scrollHeight;
}

平滑滚动到指定位置

window.scrollTo({
  top: 500, // 滚动到距离顶部500px的位置
  behavior: "smooth" // 平滑滚动
});

固定导航栏

window.addEventListener("scroll", () => {
  const nav = document.getElementById("navbar");
  if (window.scrollY > 100) {
    nav.classList.add("fixed"); // 滚动超过100px时固定导航栏
  } else {
    nav.classList.remove("fixed");
  }
});

思路

(1) Vue 指令定义
mounted 钩子:

  • 监听 click 和 focus 事件,确保下拉框展开后能够正确绑定滚动事件。

  • 使用 nextTick 确保 DOM 更新完成后再查询下拉框元素。

  • 调用 setupScrollListener 绑定滚动事件。
    beforeUnmount 钩子:

  • 移除所有事件监听器,避免内存泄漏。

  • 清理 click、focus 和 scroll 事件。

(2) 滚动监听逻辑
setupScrollListener 函数:

  • 防抖处理:使用 lodash-es 的 debounce 函数,避免频繁触发回调(设置 200ms 的防抖间隔)。
    滚动到底部判断:
  • 当 scrollTop + clientHeight >= scrollHeight - 50 时,认为滚动到底部(预留 50px 的缓冲区域)。
    回调触发:如果绑定的值是一个函数(binding.value),则调用它。
    (3)为什么监听 click 和 focus 事件
    el-select 下拉框的展开可能通过点击或键盘操作(如 Tab 键)触发。
    通过监听这两种事件,确保无论以何种方式展开下拉框,都能正确绑定滚动事件。

(4) 滚动到底部的条件

  • scrollTop:当前滚动位置。
  • clientHeight:可视区域高度。
  • scrollHeight:内容总高度。
  • 条件:scrollTop + clientHeight >= scrollHeight - 50,表示距离底部还有 50px 时触发回调。

网站公告

今日签到

点亮在社区的每一天
去签到