奇葩需求之回车切换输入框2

上篇文章: https://blog.csdn.net/wqq99/article/details/129428771

在上篇文章中,我们已经基本实现了回车切换输入框的效果,但是仍然还会存在一些问题,首先我们来完成同时按住shift和回车切换到上一个的功能。

我们只需要在聚焦切换前判断当前事件的shiftKey是否为true,如果为true设置num为-1,否则为1,然后切换焦点的下一项就是index+num,这样就能实现切换到上一个的功能:

this.inputDom.forEach((vm, index) => {
    vm.$el.addEventListener('keydown', e => {
        if (e.key == 'Enter') {
            this.inputDom[index] && this.inputDom[index].blur()
            this.inputDom[index] && this.inputDom[index].handleClose && this.inputDom[index].handleClose()
            let num = e.shiftKey ? -1 : 1
            this.inputDom[index + num] && this.inputDom[index + num].visible !== undefined && (this.inputDom[index + num].visible = true)
            this.inputDom[index + num] && this.inputDom[index + num].focus()
            if (this.inputDom[index + num].$options._componentTag == 'el-date-picker') {
                let input = [...this.inputDom[index + num].$el.children].find(item => item.localName == 'input')
                input && input.focus()
            }
        }
    })
})
window.addEventListener('keydown', (e) => {
    if (e.key == 'Enter') {
        let index = this.inputDom.findIndex(item => {
            return item.$el == e.target || this.isChild(item.$el.children, e.target)
        })
        let num = e.shiftKey ? -1 : 1
        this.inputDom[index] && this.inputDom[index].blur()
        this.inputDom[index] && this.inputDom[index].handleClose && this.inputDom[index].handleClose()
        this.inputDom[index + num] && this.inputDom[index + num].visible !== undefined && (this.inputDom[index + num].visible = true)
        this.inputDom[index + num] && this.inputDom[index + num].focus()
        if (this.inputDom[index + num] && this.inputDom[index + num].$options._componentTag == 'el-date-picker') {
            let input = [...this.inputDom[index + num].$el.children].find(item => item.localName == 'input')
            input && input.focus()
        }
    }
})

然后我们可以先将代码进行优化,对里面类似的方法进行封装,并在组件销毁时对事件监听进行移除:

export const myMixin = {
    data() {
        return {
            inputDom: "",
            nowIndex: -1,
            key: "Enter",
            checkType: {},
        }
    },
    created() {
        let _this = this
        for (let i = 0, type; type = ['Undefined', 'Null', 'Boolean', 'Number', 'String', 'Function', 'Array', 'Object'][i++];) {
            (function (type) {
                _this.checkType['is' + type] = function (obj) {
                    return Object.prototype.toString.call(obj) === '[object ' + type + ']';
                };
            })(type);
        }
    },
    mounted() {
        let typeArr = ['el-select', 'el-input', 'el-time-select', 'el-date-picker']
        this.inputDom = this.getChildComponent(this, typeArr)
        this.addWindowEvent()
        this.addDomEvent()
    },
    methods: {
        // 根据传入的组件和需要的组件类型数组,找到类型在数组中的子组件
        getChildComponent(vueInstance, componentTagArr) {
            let component = [];
            // 找到当前组件下的所有子组件
            let allComp = this.getAllChildComp(vueInstance, componentTagArr);
            allComp.forEach(vm => {
                if (componentTagArr.indexOf(vm.$options._componentTag) > -1) {
                    component.push(vm)
                }
            })
            return component;
        },
        // 获取节点的所有子孙节点
        getAllChildComp(vueInstance, componentTagArr) {
            let allComp = [], child;
            if (this.checkType.isObject(vueInstance)) {
                child = vueInstance.$children;
            } else if (this.checkType.isArray(vueInstance)) {
                child = vueInstance;
            }
            for (let i = 0; i < child.length; i++) {
                allComp.push(child[i]);
                // 后面的判断是因为下拉框或日期选择已经满足条件了,就不再继续向下寻找
                if (child[i].$children.length > 0 && componentTagArr.indexOf(child[i].$options._componentTag) == -1) {
                    // if (child[i].$children.length > 0 && child[i].$options._componentTag !== 'el-select') {
                    allComp = allComp.concat(this.getAllChildComp(child[i].$children, componentTagArr))
                };
            }
            return allComp;
        },
        addWindowEvent() {
            window.addEventListener('keydown', this.windowKeyDown.bind(this))
        },
        addDomEvent() {
            this.inputDom.forEach((vm, index) => {
                vm.$on("focus", () => {
                    this.nowIndex = index
                })
                vm.$el.addEventListener('keydown', this.keyDown.bind(this))
            })
        },
        removeWindowEvent() {
            window.removeEventListener('keydown', this.windowKeyDown)
        },
        removeDomEvent() {
            this.inputDom.forEach((vm) => {
                vm.$el.removeEventListener('keydown', this.keyDown)
            })
        },
        windowKeyDown(e) {
            if (e.key == this.key || e.code == this.key || e.keyCode == this.key) {
                let index = this.nowIndex == -1 ? this.inputDom.findIndex(item => {
                    return item.$el == e.target || this.isChild(item.$el.children, e.target)
                }) : this.nowIndex
                let num = e.shiftKey ? -1 : 1
                document.activeElement.localName == 'body' && this.changeFocus(index, num)
            }
        },
        keyDown(e) {
            if (e.key == this.key || e.code == this.key || e.keyCode == this.key) {
                let index = this.inputDom.findIndex(item => {
                    return item.$el == e.target || this.isChild(item.$el.children, e.target)
                })
                let num = e.shiftKey ? -1 : 1
                this.changeFocus(index, num)
            }
        },
        changeFocus(index, num) {
            this.inputDom[index] && this.inputDom[index].blur()
            this.inputDom[index] && this.inputDom[index].handleClose && this.inputDom[index].handleClose()
            if (this.inputDom[index + num]) {
                this.inputDom[index + num].visible !== undefined && (this.inputDom[index + num].visible = true)
                this.inputDom[index + num].focus()
                if (this.inputDom[index + num].$options._componentTag == 'el-date-picker') {
                    let input = [...this.inputDom[index + num].$el.children].find(item => item.localName == 'input')
                    input && input.focus()
                }
                this.nowIndex = index + num
            } else {
                this.nowIndex = -1
            }
        },
        isChild(children, dom) {
            children = [...children]
            let a = children.some(item => {
                return item == dom || this.isChild(item.children, dom)
            })
            return a
        },
    },
    beforeDestroy() {
        window.removeEventListener('keydown', this.windowKeyDown)
        this.inputDom.forEach((vm) => {
            vm.$el.removeEventListener('keydown', this.keyDown)
        })
    }
}

上面代码除了是封装之后的,还解决了另外一个问题,那就是在通过鼠标选择某个组件输入或选择后失焦,再次回车会从第一项开始的问题,解决的办法也很简单,就是在键盘事件的聚焦和每个组件的focus事件中将当前组件的下标存下来(nowIndex),下次回车就从nowIndex的下一项开始:

至此整个功能就完成了,感觉会比较麻烦,而且可能还会存在一些问题,如果大家有更好的方法欢迎在评论区留言。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值