前言
在一些表单中,为了优化用户体验,通常需要在用户按下enter键之后跳到下一个input框或者button中。
对于js,可以选择获取表单中的所有input 和button后逐个添加键盘enter事件,现在,我们将这个逻辑封装为一个抽象组件,使得下面的代码能够实现这个效果:
<sss-nexter>
<el-input></el-input>
<el-input></el-input>
<sss-button type="main" round>click me</sss-button>
</sss-nexter>
20230418_191419
您的浏览器不支持 HTML5 video 标签。
因为这个组件不需要渲染任何节点(这意味这组件本身不会被渲染到真实dom中,但是组件render函数创建的vnode会被渲染),只提供功能,所以作为抽象组件最合适不过。
抽象组件
在vue中为我们提供的抽象组件有:、
、
等
他们的实现是一个对象,用于一个共同属性abstract: Boolean。当该属性为true时表明该组件时应该抽象组件。
通过抽象组件我们可以对其内部vnode做一些有趣的事情。
毫无疑问,在当前抽象组件构造函数中,我们访问this将会是会创建出来的实例。
我们在render函数中打印this时:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K1qfnYQV-1681820342974)(C:\Users\laster\Desktop\md\算法\643e82950d2dde5777be4ec8.jpg)]
会得到
接下来我们介绍即将用到的东西
在这之前,请先了解render函数。推荐一篇文章:[Vue抽象组件——所涉及的知识点](Vue抽象组件——所涉及的知识点 - 掘金 (juejin.cn))
-
this.$el:
该属性存储了组件自身节点渲染完毕之后对应真实dom节点的引用(由此,只有在自身渲染完毕之后才能访问到$el属性,一般是mounted)
-
this.$slots:
相信用过slot的都不会陌生这个属性,该属性代表了通过槽传入组件的vnod,在我们最开始演示的时候那写法,其实可以写成:
<sss-nexter> <slot name="default"> <el-input></el-input> <el-input></el-input> <sss-button type="main" round>click me</sss-button> </slot> </sss-nexter>
通常我们在不使用slot时,其实是写在了默认slot当中,通过这个属性我们可以访问到所有传入组件的vnode
这个属性具有很多高阶用法,如果想具体了解可以参考这篇文章:[Vue 插槽(slot)使用(通俗易懂)](Vue 插槽(slot)使用(通俗易懂) - 掘金 (juejin.cn)) (不看也不影响我们继续)
code
render
我们知道,在vue2中,一个组件只能渲染一个根元素,所以通常情况下,我们的组件都会被最外层div包裹,所以在抽象组件中,我们也要遵循这个规则。
那要是忘了写或者用的时候不想写怎么办?
那就需要我们手动添加一层div了,所以在render函数中:
render() {
const slots = this.$slots.default
return slots.length > 1 ? h('div', slots) : slots
},
在 Vue 中,h
函数是一个用来创建虚拟节点(VNode)的工具函数,它的全称是 createElement
, 在这里我们只需要稍微了解h函数的作用就行。
mounted
在mounted函数中,我们通过this.$el
来获取对应真实dom节点,在通过css选择器querySelectorAll
来获取所有的input、button元素。
之后为每一个元素添加键盘enter
事件即可。
当我在这里添加了键盘事件,在其他地方又添加了键盘事件,两者会覆盖么?
并不会,两者都会添加到该元素的键盘事件当中。
mounted() {
//获取聚焦器内部所有特定元素
const aimList = this.$el.querySelectorAll("input, button")
//为元素添加事件
for (let i = 0; i < aimList.length - 1; i++) {
aimList[i].addEventListener("keyup", (e) => {
if (e.key === 'Enter') {
e.preventDefault();
const next = i + 1;
aimList[next].focus();
}
})
}
//最后一个元素按下时失去焦点
aimList[aimList.length-1].addEventListener("keyup",(e) => {
if (e.key === 'Enter') {
aimList[aimList.length-1].blur();
}
})
}
完整code
/*
聚焦器: 当input元素按下enter之后自动聚焦下一个元素
*/
import {h} from "vue"
export default {
name: "sss-nexter",
abstract: true,
render() {
const slots = this.$slots.default
return slots.length > 1 ? h('div', slots) : slots
},
mounted() {
//获取聚焦器内部所有特定元素
const aimList = this.$el.querySelectorAll("input, button")
//为元素添加事件
for (let i = 0; i < aimList.length - 1; i++) {
aimList[i].addEventListener("keyup", (e) => {
if (e.key === 'Enter') {
e.preventDefault();
const next = i + 1;
aimList[next].focus();
}
})
}
//最后一个元素按下时失去焦点
aimList[aimList.length-1].addEventListener("keyup",(e) => {
if (e.key === 'Enter') {
aimList[aimList.length-1].blur();
}
})
}
}
end
作为第一个抽象组件,简单就行,主要为了了解抽象组件的写法。
if (e.key === 'Enter') {
aimList[aimList.length-1].blur();
}
})
}
}
# end
作为第一个抽象组件,简单就行,主要为了了解抽象组件的写法。
感谢看到最后😀