浅谈vue限制文本框输入数字的正确姿势


Posted in Javascript onSeptember 02, 2019

最近遇到一个需求,需要限制文本框输入数字,而number类型的输入框有箭头,个人不是很喜欢,因此想要寻求其它途径实现。本想通过网上找个现成的插件,然而百度,谷歌一番都没有找到满意的答案,至于随手一搜出来的方案或多或少都有点缺陷。因此自己动手,丰衣足食。

事件选型

首先我们很容易想到通过事件来达到目的,大致可以通过以下几个事件来实现:

  • keypress/keydown

思路:按键按下的时候触发,通过判断按下的是否是数字返回true/false来限制用户的输入。
缺点:无法限制用户复制粘贴的数据。

  • keyup

思路:同keypress事件,区别在于按键摁下弹起的时候触发。
缺点:从界面上看有个回退(显示用户输入的 不合法字符之后删除回退)的现象,不是很自然。

  • input

思路:用户输入的时候触发
优点:可以在用户复制粘贴的时候获取到完整的值,同时不会有界面回退的现象。

初步实现

综合各种方案的优缺点,选定input事件来实现,首先我们来实现最简单的限制用户只能输入正整数。代码如下:

<el-input v-model="model" oninput="value=value.replace(/[^\d]/g, '')" />

但是,此方法有的时候没有很好地达到预期效果,这边我放到最后再来讲。

封装成指令

通过自定义指令来增加相应DOM的特性,使其支持文本输入限制。

function onInput(el, ele, binding, vnode) {
 function handle() {
 // 只保留数字
 ele.value = ele.value.replace(/[^\d]/g, '')
 } 

 return handle
}
const numberInput= {
 bind(el, binding, vnode) {
 const ele = el.tagName === 'INPUT' ? el : el.querySelector('input')
 ele.addEventListener('input', onInput(el, ele, binding, vnode), false)
 },
}
Vue.directive('number-input', numberInput)

使用方法:

<el-input v-model="model" v-number-input />

支持浮点数

我们知道v-model指令有三种修饰符lazy,  number, trim实现了不同的功能,这边呢,也想通过v-number-input.float的方式达到支持浮点数的目的 。

修改handle回调,增加浮点数的支持

function handle() {
+ let val = ele.value
+ // modifiers为修饰符对象,传入了float,则其float属性为true
+ if (binding.modifiers.float) {
+ // 清除"数字"和"."以外的字符
+ val = val.replace(/[^\d.]/g, '')
+ // 只保留第一个, 清除多余的
+ val = val.replace(/\.{2,}/g, '.')
+ // 第一个字符如果是.号,则补充前缀0
+ val = val.replace(/^\./g, '0.')
+ } else {
 val = ele.value.replace(/[^\d]/g, '')
 }
 ele.value = val
}

使用方法:

<el-input v-model="model" v-number-input.float />

支持小数保留位最大位数限制

查阅官方文档可知,指令支持参数的传递,这边我们期望通过参数传递小数点保留位数的配置

function handle() {
 let val = ele.value
 // modifiers为修饰符对象,传入了float,则其float属性为true
 if (binding.modifiers.float) {
 // 清除"数字"和"."以外的字符
 val = val.replace(/[^\d.]/g, '')
 // 只保留第一个, 清除多余的
 val = val.replace(/\.{2,}/g, '.')
 // 第一个字符如果是.号,则补充前缀0
 val = val.replace(/^\./g, '0.')
+ if (typeof binding.value !== 'undefined') {
+ // 期望保留的最大小数位数
+ let pointKeep = 0
+ if (typeof binding.value === 'string' 
+  || typeof binding.value === 'number') {
+  pointKeep = parseInt(binding.value)
+ }
+ if (!isNaN(pointKeep)) {
+  if (!Number.isInteger(pointKeep) 
+   || pointKeep < 0) {
+  pointKeep = 0
+  }
+  const str='^(\\d+)\\.(\\d\{' + pointKeep + '}).*$'
+  const reg=new RegExp(str)
+  if (pointKeep === 0) {
+  // 不需要小数点
+  val = val.replace(reg, '$1')
+  } else {
+  // 通过正则保留小数点后指定的位数
+  val = val.replace(reg, '$1.$2')
+  }
+ }
 } else {
 val = ele.value.replace(/[^\d]/g, '')
 }
 ele.value = val
}

使用方法:

// 最多支持保留2位小数
<el-input v-model="model" v-number-input.float="2" />

支持负数

对正则稍加修改,只保留开头的负号,使其支持负数

function handle() {
 let val = ele.value
 // modifiers为修饰符对象,传入了float,则其float属性为true
 if (binding.modifiers.float) {
 // 清除"数字"和"."以及"-"以外的字符
+ val = val.replace(/[^\-\d.]/g, '')
 // 只保留第一个'-'号
+ val = val.replace(/\.{2,}/g, '.').replace(/\-{2,}/g, '-')
 // 将 '-.' 替换成 '-0.'
+ val = val.replace(/^\./g, '0.').replace(/^\-\./, '-0.')
 if (typeof binding.value !== 'undefined') {
 // 期望保留的最大小数位数
 let pointKeep = 0
 if (typeof binding.value === 'string' 
   || typeof binding.value === 'number') {
  pointKeep = parseInt(binding.value)
 }
 if (!isNaN(pointKeep)) {
  if (!Number.isInteger(pointKeep) 
   || pointKeep < 0) {
  pointKeep = 0
  }
  // 增加'-'号的支持
+ const str='^(\\-)*(\\d+)\\.(\\d\{' + pointKeep + '}).*$'
  const reg=new RegExp(str)
  if (pointKeep === 0) {
  // 不需要小数点
+  val = val.replace(reg, '$1$2')
  } else {
  // 通过正则保留小数点后指定的位数
+  val = val.replace(reg, '$1$2.$3')
  }
 }
 } else {
 val = ele.value.replace(/[^\d]/g, '')
 }
 ele.value = val
}

支持输入的最大值和最小值的限制

指令的参数支持对象的传递,因此这边我们通过对象的字段配置最大数和最小值的配置。定义如下:

{
min: {type: number},
max: {type: number},
// 最大小数位,仅在float下有效
decimal: {type: number}
}
function handle() {
 let val = ele.value
 // modifiers为修饰符对象,传入了float,则其float属性为true
 if (binding.modifiers.float) {
 // 清除"数字"和"."以及"-"以外的字符
 val = val.replace(/[^\-\d.]/g, '')
 // 只保留第一个'-'号
 val = val.replace(/\.{2,}/g, '.').replace(/\-{2,}/g, '-')
 // 将 '-.' 替换成 '-0.'
 val = val.replace(/^\./g, '0.').replace(/^\-\./, '-0.')
 if (typeof binding.value !== 'undefined') {
 // 期望保留的最大小数位数
 let pointKeep = 0
 if (typeof binding.value === 'string' 
   || typeof binding.value === 'number') {
  pointKeep = parseInt(binding.value)
+ } else if (typeof binding.value === 'object') {
+  // 支持新的小数点保留位配置
+  pointKeep = binding.value.decimal
 }
 if (!isNaN(pointKeep)) {
  if (!Number.isInteger(pointKeep) 
   || pointKeep < 0) {
  pointKeep = 0
  }
  // 增加'-'号的支持
  const str='^(\\-)*(\\d+)\\.(\\d\{' + pointKeep + '}).*$'
  const reg=new RegExp(str)
  if (pointKeep === 0) {
  // 不需要小数点
  val = val.replace(reg, '$1$2')
  } else {
  // 通过正则保留小数点后指定的位数
  val = val.replace(reg, '$1$2.$3')
  }
 }
 } else {
 val = ele.value.replace(/[^\d]/g, '')
 }
+ if (val !== '' && typeof binding.value === 'object') {
+ let { min, max } = binding.value
+ min = parseFloat(min)
+ max = parseFloat(max)
+ if (!isNaN(min)) {
+ if (min >= 0) {
+  // 不能是负数
+  val = val.replace('-', '')
+ }
+ if (parseFloat(val) < min) {
+  val = min
+ }
+ }
+ if (!isNaN(max)) {
+ if (parseFloat(val) > max) {
+  val = max
+ }
+ }
+ }
 ele.value = val
}

使用方法:

// 最小为0
<el-input v-model="model" v-number-input="{ min: 0 }" />

结语

至此我们的功能已经实现得差不多了,上述代码已经足以应对我们的大多数的需求了。但在我发现某些场景下有的时候键入非数字时,表单验证获取的数值仍然是我键入的字符而非空值,导致非空验证不正确。但是这种情况并非必现的,我猜想可能是因为是vue的model数据未同步导致的,因此在上述回调函数添加上以下代码,手动触发数据的双向绑定。

if (vnode.componentInstance) {
 vnode.componentInstance.$emit('input', ele.value)
} else {
 vnode.elm.dispatchEvent(new CustomEvent('input', ele.value))
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript 火狐(firefox)不显示本地图片问题解决
Jul 05 Javascript
HTML颜色选择器实现代码
Nov 23 Javascript
js制作的鼠标悬浮时产生的下拉框效果
Oct 27 Javascript
JS的replace方法详细介绍
Nov 09 Javascript
setTimeout函数兼容各主流浏览器运行执行效果实例
Jun 13 Javascript
js实现弹窗插件功能实例代码分享
Dec 12 Javascript
js打开windows上的可执行文件示例
May 27 Javascript
jquery中animate的stop()方法作用实例分析
Jan 30 Javascript
jquery 表单验证之通过 class验证表单不为空
Nov 02 Javascript
原生态js,鼠标按下后,经过了那些单元格的简单实例
Aug 11 Javascript
D3.js实现雷达图的方法详解
Sep 22 Javascript
react-router中的属性详解
Jun 01 Javascript
Layui 带多选框表格监听事件以及按钮自动点击写法实例
Sep 02 #Javascript
layer父页获取弹出层输入框里面的值方法
Sep 02 #Javascript
详解element-ui表格中勾选checkbox,高亮当前行
Sep 02 #Javascript
详解基于Vue的支持数据双向绑定的select组件
Sep 02 #Javascript
layui当点击文本框时弹出选择框,显示选择内容的例子
Sep 02 #Javascript
Vue内部渲染视图的方法
Sep 02 #Javascript
一步一步实现Vue的响应式(对象观测)
Sep 02 #Javascript
You might like
php获得用户ip地址的比较不错的方法
2014/02/08 PHP
php调用云片网接口发送短信的实现方法
2017/10/25 PHP
jquery1.4后 jqDrag 拖动 不可用
2010/02/06 Javascript
javascript学习笔记(十六) 系统对话框(alert、confirm、prompt)
2012/06/20 Javascript
jquery的冒泡事件的阻止与允许(三种实现方法)
2013/02/01 Javascript
JavaScript中的console.group()函数详细介绍
2014/12/29 Javascript
JavaScript中的setMilliseconds()方法使用详解
2015/06/11 Javascript
jQuery实现按钮只点击一次后就取消点击事件绑定的方法
2015/06/26 Javascript
JS简单实现多级Select联动菜单效果代码
2015/09/06 Javascript
移动端使用localStorage缓存Js和css文的方法(web开发)
2016/09/20 Javascript
js实现上下左右弹框划出效果
2017/03/08 Javascript
vue中for循环更改数据的实例代码(数据变化但页面数据未变)
2017/09/15 Javascript
webpack3之loader全解析
2017/10/26 Javascript
微信小程序基于slider组件动态修改标签透明度的方法示例
2017/12/04 Javascript
vue路由拦截及页面跳转的设置方法
2018/05/24 Javascript
JS实现判断有效的数独算法示例
2019/02/25 Javascript
基于vue 动态菜单 刷新空白问题的解决
2020/08/06 Javascript
python 2.7.13 安装配置方法图文教程
2018/09/18 Python
详解Numpy中的数组拼接、合并操作(concatenate, append, stack, hstack, vstack, r_, c_等)
2019/05/27 Python
Python搭建Spark分布式集群环境
2019/07/05 Python
PyCharm 2019.3发布增加了新功能一览
2019/12/08 Python
PyCharm刷新项目(文件)目录的实现
2020/02/14 Python
python修改linux中文件(文件夹)的权限属性操作
2020/03/05 Python
python使用hdfs3模块对hdfs进行操作详解
2020/06/06 Python
基于CentOS搭建Python Django环境过程解析
2020/08/24 Python
详解Python流程控制语句
2020/10/28 Python
基于DOM+CSS3实现OrgChart组织结构图插件
2016/03/02 HTML / CSS
纯css3制作煽动翅膀的蝴蝶的示例
2018/04/23 HTML / CSS
英国历史最悠久的DJ设备供应商:DJ Finance、DJ Warehouse、The DJ Shop
2019/09/04 全球购物
征婚广告词
2014/03/17 职场文书
环保倡议书500字
2014/05/15 职场文书
中考标语大全
2014/06/05 职场文书
工作违纪检讨书范文
2015/01/26 职场文书
房屋所有权证明
2015/06/19 职场文书
Pytorch 统计模型参数量的操作 param.numel()
2021/05/13 Python
Windows和Linux上部署Golang并运行程序
2022/04/22 Servers