阅读element-ui源码之select选择器下拉菜单如何关闭

1.前言

element-ui框架的select组件,点击后,会出现下拉菜单。当我们点击页面上除选择框和下拉菜单的区域时,下拉菜单会关闭,那么,element是如何实现的呢?

2.逻辑解读

翻开源码目录后,找到了clickoutside.js。该文件就是处理鼠标是否点击相应元素外面的。之所以这么说,是因为这是个公共处理方法,除了select选择器,还有其他组件也用到的。

对于判断鼠标点击元素之外,element使用了vue自定义指令来实现。
在这里插入图片描述
好处就是有相应的钩子函数,从元素绑定,元素更新,元素销毁,都能够自动化处理,只需要在相应的钩子函数中写逻辑即可。

那么,点击事件时如何添加的呢?
element是为整个文档添加了鼠标按下,鼠标弹起事件。

!Vue.prototype.$isServer && on(document, 'mousedown', e => (startClick = e));

!Vue.prototype.$isServer && on(document, 'mouseup', e => {
  nodeList.forEach(node => node[ctx].documentHandler(e, startClick));

那么,判断具体是如何实现的呢?
它在自定义指令的bind钩子中,为元素添加了createDocumentHandler方法。该方法是整个判断逻辑的核心。代码解释写在相应的地方了。

// 点击的话,判断两个地方,一个是ElSelect,另一个是ElSelectDropdown
function createDocumentHandler(el, binding, vnode) {
  return function(mouseup = {}, mousedown = {}) {
    if (!vnode ||
      !vnode.context ||
      !mouseup.target ||
      !mousedown.target ||
      // contains:判断鼠标点击的dom是否为el的后代节点
      el.contains(mouseup.target) ||
      el.contains(mousedown.target) ||
      el === mouseup.target ||
      // popperElm:是ElSelectDropdown组件DOM
      // context是VNode的一个属性,其值可为component/void
      (vnode.context.popperElm &&
      (vnode.context.popperElm.contains(mouseup.target) ||
      vnode.context.popperElm.contains(mousedown.target)))) return;

    if (binding.expression &&
      el[ctx].methodName &&
      vnode.context[el[ctx].methodName]) {
      console.log('外面点击');
      vnode.context[el[ctx].methodName]();
    } else {
      el[ctx].bindingFn && el[ctx].bindingFn();
    }
  };
}

要判断就是两个地方,一个选择框,一个下拉菜单。如果都不是,那么就关闭下拉菜单。这里用了Node.contains()api,判断传入节点是否为该节点的后代节点。下面是MDN解释:
在这里插入图片描述
需要特别注意:鼠标点击和弹起的地方(弹起和点击的地方一定要相同,这里是非常需要注意的),不是选择框和下拉菜单区域的话,就关闭下拉菜单。

  • 点击区域是否为选择框
el.contains(mouseup.target) ||
el.contains(mousedown.target) ||
el === mouseup.target ||
  • 是否为下拉菜单区域
(vnode.context.popperElm &&
(vnode.context.popperElm.contains(mouseup.target) ||
vnode.context.popperElm.contains(mousedown.target))))
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要实现下拉触底加载下一页,可以使用 `element-ui` 的 `el-select` 和 `el-option` 组件结合 `scroll` 事件来实现。具体操作如下: 1. 在 `el-select` 组件中添加 `@scroll.native` 事件,绑定一个方法,用于监听下拉的滚动事件。 2. 在绑定的方法中,判断下拉是否滚动到底部,如果滚动到底部,则触发加载下一页的方法,并将下一页的数据添加到选项中。 3. 在 `el-option` 组件中添加一个指令 `v-if`,用于判断当前选项是否需要显示。当选项数据为空时,不显示该选项。 下面是一个示例代码: ```html <template> <el-select v-model="value" @scroll.native="handleScroll" :filterable="filterable"> <el-option v-for="option in options" :key="option.value" :label="option.label" :value="option.value" v-if="option.label !== 'loading'"></el-option> <el-option v-if="loading" label="loading"> <template #loading> <i class="el-icon-loading"></i> </template> </el-option> </el-select> </template> <script> export default { data() { return { value: '', options: [], loading: false, filterable: true, page: 1, }; }, methods: { handleScroll(e) { const container = e.target; const scrollHeight = container.scrollHeight; const offsetHeight = container.offsetHeight; const scrollTop = container.scrollTop; if (scrollTop + offsetHeight >= scrollHeight && !this.loading) { this.loading = true; this.loadNextPage(); } }, async loadNextPage() { // 模拟异步加载数据 await new Promise((resolve) => setTimeout(resolve, 1000)); const nextPage = this.page + 1; const newOptions = Array.from({ length: 10 }, (_, index) => ({ label: `Option ${nextPage}${index + 1}`, value: `option${nextPage}${index + 1}`, })); this.options = this.options.concat(newOptions); this.page = nextPage; this.loading = false; }, }, }; </script> ``` 这样,当下拉滚动到底部时,就会触发加载下一页的方法,并将下一页的数据添加到选项中,从而实现了下拉触底加载下一页的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值