上篇文章: 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的下一项开始:
至此整个功能就完成了,感觉会比较麻烦,而且可能还会存在一些问题,如果大家有更好的方法欢迎在评论区留言。