0


Element UI的el-cascader级联选择器为文字绑定联动,实现点击文字即选中

先上效果图

在最新的 Element UI 或 Element Plus 版本中,

el-cascader

组件不支持通过点击文字选中项的原因,可能涉及几个关键因素:

  1. 设计简化: 该组件默认的交互是点击复选框或单选框来选择选项,而不是点击文字。这种设计可以减少误选项的可能性,避免在多级菜单中用户不小心点击文字而误选。
  2. 避免冲突和误操作: 在多级菜单中,点击文字可能导致意外的展开、收起,或选择,尤其是当用户只是想展开子菜单但意外选中了当前项。这种设计选择可能是为了减少误操作,增强对复杂层级结构的控制。
  3. 性能优化: 增加对文本的点击事件监听会涉及更多的 DOM 操作。每次级联项展开都需要重新绑定事件监听器,频繁的 DOM 操作可能影响性能,特别是在复杂多级数据结构中。
  4. 一致性: Element UI 和 Element Plus 遵循单向数据流的原则,尤其是在表单组件中。组件交互行为的一致性可以让用户更好地理解和操作,默认只在复选框和单选框上触发点击选择,这种一致性简化了操作体验。

如何实现文字点击选中

尽管默认行为不支持点击文字选中,但可以用 JavaScript 和 DOM 操作来手动实现,如前述代码示例,通过监听标签的

click

事件,找到对应的复选框或单选框并触发点击:

  • @expand-change 事件中设置当前层级后,在下一次 DOM 更新后调用 setCascaderDomEvent
  • setCascaderDomEvent 中绑定 .el-cascader-node__label 标签的 click 事件,调用 cascaderCheckEvent 手动触发输入框的点击事件,从而实现文字选中效果。

这样的方法虽然可以达到效果,但可能需要根据新版本中 DOM 结构的变化进行适配。

方法如下

1.

expandChange(event)
expandChange(event) {
      this.currentLevel = event.length
      this.$nextTick(() => {
        this.setCascaderDomEvent()
      })
    }
  • **event.length: **expandChange 事件在展开或收起级联菜单时触发,event 参数是一个包含当前展开的级别数组。event.length 就是当前展开的级别数,存储到 this.currentLevel 中,以便后续操作。
  • this.$nextTick:this.$nextTick() 是 Vue.js 提供的一个方法,用于在 DOM 更新后执行某个操作。Vue 可能会对 DOM 进行异步更新,$nextTick 可以确保在下一次 DOM 更新循环中执行提供的回调函数。这是因为在展开/收起菜单后,DOM 元素可能需要时间进行渲染,因此延迟调用以确保可以正确访问最新的 DOM 结构。
  • setCascaderDomEvent();:在 $nextTick 的回调函数中,调用 setCascaderDomEvent 方法。这个方法的功能是在级联选择器中为相应的节点绑定点击事件,确保这些节点在 UI 上可交互。由于是在 $nextTick 中调用的,因此可以确保在 DOM 已更新的情况下执行这一绑定操作,这样可以避免因节点未渲染完成而导致的事件绑定失败。

2.

setCascaderDomEvent()
setCascaderDomEvent() {
      let cascaderDom = document.querySelectorAll(
        '.special-cascader .el-cascader-menu__list'
      )
      if (cascaderDom.length >= this.currentLevel - 1) {
        let optionDom = cascaderDom[this.currentLevel]
        optionDom.querySelectorAll('.el-cascader-node__label').forEach((label) => {
            let nextDom = label.nextElementSibling
            label.addEventListener('click', this.cascaderCheckEvent)
            if (!nextDom) {
                label.style.cursor = 'pointer'
            }
        })
      }
      if (this.currentLevel) {
        let optionDom = cascaderDom[this.currentLevel - 1]
        optionDom.querySelectorAll('.el-cascader-node__label').forEach((label) => {
            let nextDom = label.nextElementSibling
            label.addEventListener('click', this.cascaderCheckEvent)
            if (!nextDom) {
                label.style.cursor = 'pointer'
            }
        })
      }
    }
  • document.querySelectorAll('.special-cascader .el-cascader-menu__list'): 选择当前级联菜单的所有列表项。special-cascader 是通过 popper-class 属性设置的类名,用于特定样式或操作。
  • if (cascaderDom.length >= this.currentLevel - 1): 确保当前级别的菜单存在。this.currentLevel - 1 用于计算数组索引,因为 currentLevel 是基于级别的,但 DOM 数组索引是从 0 开始的。
  • let optionDom = cascaderDom[this.currentLevel]: 选择当前级别的 DOM 元素。
  • optionDom.querySelectorAll('.el-cascader-node__label'): 查找当前菜单级别下的所有标签。
  • label.addEventListener('click', this.cascaderCheckEvent): 为每个标签添加点击事件,点击标签时将触发 cascaderCheckEvent 方法。
  • if (!nextDom): 检查标签后是否有下一个兄弟元素。若没有,表示当前标签是最后一个选项,设置鼠标光标为指针,增强用户体验。

3.

cascaderCheckEvent(event)
cascaderCheckEvent(event) {
    let brother = event.target.previousElementSibling; // 获取前一个兄弟元素
    const input = brother.querySelector('input[type="checkbox"], input[type="radio"]'); // 支持复选框和单选框
    if (input) {
        input.click(); // 模拟点击复选框
    }

    const childMenu = event.target.nextElementSibling; // 获取下一个兄弟元素
    if (childMenu && childMenu.classList.contains('el-cascader-menu__list')) {
        // 这里可以根据需求添加展开/收起的逻辑
    }
},
  • let brother = event.target.previousElementSibling: 获取当前点击标签的前一个兄弟元素,该元素通常是输入框(复选框或单选框)。
  • const input = brother.querySelector('input[type="checkbox"], input[type="radio"]'): 查找输入框。支持复选框和单选框。
  • if (input): 确保输入框存在。如果存在,则模拟点击输入框,实际操作上选中该项。
  • const childMenu = event.target.nextElementSibling: 获取当前标签的下一个兄弟元素,如果存在,则可能是子菜单。
  • if (childMenu && childMenu.classList.contains('el-cascader-menu__list')): 检查下一个元素是否是子菜单,可以根据需求添加逻辑来展开或收起子菜单(此逻辑在此代码片段中未具体实现)。

完整代码

<template>
    <el-cascader
        popper-class="special-cascader"
        :options="options"
        :props="{
          checkStrictly: true,
          expandTrigger: 'hover'
        }"
        placeholder="请选择组织"
        @change="handleChange"
        @expand-change="expandChange">
      </el-cascader>
</template>
<script>
    export default {
        data() {
            return {
                value: [],
                currentLevel: 0,
                props: {
                    multiple: true
                },
                options: [{
                    value: 1,
                    label: '东南',
                    children: [{
                        value: 2,
                        label: '上海',
                        children: [{
                                value: 3,
                                label: '普陀'
                            },
                            {
                                value: 4,
                                label: '黄埔'
                            },
                            {
                                value: 5,
                                label: '徐汇'
                            }
                        ]
                    }, {
                        value: 7,
                        label: '江苏',
                        children: [{
                                value: 8,
                                label: '南京'
                            },
                            {
                                value: 9,
                                label: '苏州'
                            },
                            {
                                value: 10,
                                label: '无锡'
                            }
                        ]
                    }, {
                        value: 12,
                        label: '浙江',
                        children: [{
                                value: 13,
                                label: '杭州'
                            },
                            {
                                value: 14,
                                label: '宁波'
                            },
                            {
                                value: 15,
                                label: '嘉兴'
                            }
                        ]
                    }]
                }, {
                    value: 17,
                    label: '西北',
                    children: [{
                        value: 18,
                        label: '陕西',
                        children: [{
                                value: 19,
                                label: '西安'
                            },
                            {
                                value: 20,
                                label: '延安'
                            }
                        ]
                    }, {
                        value: 21,
                        label: '新疆维吾尔族自治区',
                        children: [{
                                value: 22,
                                label: '乌鲁木齐'
                            },
                            {
                                value: 23,
                                label: '克拉玛依'
                            }
                        ]
                    }]
                }]
            };
        },
        methods: {
            handleChange() {

            },
            expandChange(event) {
      this.currentLevel = event.length
      let that = this
      setTimeout(() => {
        that.setCascaderDomEvent()
      }, 0)
    },

    setCascaderDomEvent() {
      let cascaderDom = document.querySelectorAll(
        '.special-cascader .el-cascader-menu__list'
      )
      if (cascaderDom.length >= this.currentLevel - 1) {
        let optionDom = cascaderDom[this.currentLevel]
        optionDom.querySelectorAll('.el-cascader-node__label').forEach((label) => {
            let nextDom = label.nextElementSibling
            label.addEventListener('click', this.cascaderCheckEvent)
            if (!nextDom) {
                label.style.cursor = 'pointer'
            }
        })
      }
      if (this.currentLevel) {
        let optionDom = cascaderDom[this.currentLevel - 1]
        optionDom.querySelectorAll('.el-cascader-node__label').forEach((label) => {
            let nextDom = label.nextElementSibling
            label.addEventListener('click', this.cascaderCheckEvent)
            if (!nextDom) {
                label.style.cursor = 'pointer'
            }
        })
      }
    },

    // 子菜单label监听事件方法
    cascaderCheckEvent(event) {
      // 查找对应的输入框
      let brother = event.target.previousElementSibling // 获取前一个兄弟元素
      const input = brother.querySelector(
        'input[type="checkbox"], input[type="radio"]'
      ) // 支持复选框和单选框
      if (input) {
        input.click() // 模拟点击复选框
      }

      // 如果有子菜单,展开/收起子菜单
      const childMenu = event.target.nextElementSibling
      if (childMenu && childMenu.classList.contains('el-cascader-menu__list')) {
        // 这里可以根据需求添加展开/收起的逻辑
      }
    },
        },
    };
</script>
<style>
</style>
标签: ui vue.js 前端

本文转载自: https://blog.csdn.net/weixin_46679688/article/details/143319129
版权归原作者 河坤 所有, 如有侵权,请联系我们删除。

“Element UI的el-cascader级联选择器为文字绑定联动,实现点击文字即选中”的评论:

还没有评论