需求背景:最近项目中遇到很多输入类控件,且有频繁改动需求,如果数值过长,则需要一直按着删除键,于是想能不能获取焦点的时候,就直接选中,这样就可以直接删除了
1、思路分析
记得很早的时候做小程序,输入组件可以直接设置要选中的位数,于是想看看目前使用饿了么组件有无该功能,发现并没有提供,那就只有自己写了;接着之前的思路,想着是不是input聚焦后也需要选择选中的长度,于是查看了文档,刚好发现了这个api ,HTMLInputElement.select(),并且兼容性良好,那简单了直接开干
2、开干
有了上面这个api,那么只需要写一个指令,找到当前input节点,当其聚焦时,调用一下select方法即可,vue的自定义指令,也完美符合需求
Vue.directive('input-select', {
inserted: function (el) {
el._select_ = function (e) {
e.target.select()
}
el.addEventListener('focus',el._select_)
},
unbind: function (el) {
if (el._select_) {
input.removeEventListener('focus',el._select_,false)
}
}
})
经过简单测试,完美,没有任何问题,正当高兴时,准备给所有输入框都加上,突然发现,怎么饿了么输入框添加了之后无效呢(黑人问号脸???),于是看了下
原来,一般复杂的业务组件,输入框也会被嵌套几成的,而不是直接input标签裸奔,所有开始事件绑定就绑错了
3、优化
有了上面的问题,笔者想了一个办法,既然初始绑定不一定会帮到Input上,那么我直接找出当前节点所有input控件,挨个给他绑上,再挨个卸载,要注意一个小细节,通常开源的组件库,人家也是用原生的组件封装的,也就是说,有可能 switch 啥的也是input写的,所有我们在绑定的时候也可以按需过滤下,比如笔者这里,就指定text、number、tel、password、url、search这几种类型的输入框才绑定事件
完整如下:
Vue.directive('input-select', {
inserted: function (el) {
function addSelect(vnodes = []) {
// 指定几种类型
const types = ['text','number','tel','password','url','search']
vnodes.forEach((input) => {
if(types.includes(input.type) && !input.readOnly) {
input._select_ = function (e) {
//修复一个bug 当聚焦瞬间又失焦时,可能会造成事件多次调用
// 故,选中前判断当前元素是否聚焦
document.activeElement == e.target && e.target.select()
}
input.addEventListener('focus', input._select_, false)
el._hasInputSelect = true
}
})
}
addSelect(el.tagName == 'INPUT' ? [el] : [...el.querySelectorAll('input')])
},
unbind: function (el) {
if (el._hasInputSelect) {
(el.tagName == 'INPUT' ? [el] : [...el.querySelectorAll('input')]).forEach((input) => {
input._select_ && input.removeEventListener('focus',input._select_,false)
input._select_ = null
delete input._select_
})
delete el._hasInputSelect
}
}
})
4、总结
看似一个小功能,其实也些许问题,其实还有可以扩展,比如:部分组件如果输入控件比较多,为了节约性能,有可能输入框是动态的,即,没有点击的时候是文本,当用户点了才切换成输入框,对于这种情况,目前笔者未进行探索,只是感觉可能需要围绕vue指令的生命周期进行操作,感兴趣的朋友可自行探索