浅谈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 相关文章推荐
js 纯数字不重复排列的另类方法
Jul 17 Javascript
JavaScript浏览器选项卡效果
Aug 25 Javascript
jquery心形点赞关注效果的简单实现
Nov 14 Javascript
bootstrap提示标签、提示框实现代码
Dec 28 Javascript
JS实现iframe自适应高度的方法示例
Jan 07 Javascript
PHP实现本地图片上传和验证功能
Feb 27 Javascript
详解webpack 配合babel 将es6转成es5 超简单实例
May 02 Javascript
快速将Vue项目升级到webpack3的方法步骤
Sep 14 Javascript
浅谈js的解析顺序 作用域 严格模式
Oct 23 Javascript
浅谈vue-router 路由传参的方法
Dec 27 Javascript
js中怎么判断两个字符串相等的实例
Jan 17 Javascript
详解vue-cli3多环境打包配置
Mar 28 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函数之子字符串替换&amp;#65279; str_replace
2011/03/23 PHP
使用 PHPMAILER 发送邮件实例应用
2012/11/07 PHP
教你如何用php实现LOL数据远程获取
2014/06/10 PHP
php实现的支持imagemagick及gd库两种处理的缩略图生成类
2014/09/23 PHP
Laravel 在views中加载公共页面的实现代码
2019/10/22 PHP
JavaScript入门教程(1) 什么是JS
2009/01/31 Javascript
javascript学习笔记(七) js函数介绍
2012/06/19 Javascript
Javascript 遍历页面text控件详解
2014/01/06 Javascript
js判断日期时间有效性的方法
2015/10/24 Javascript
JS实现浏览器状态栏文字闪烁效果的方法
2015/10/27 Javascript
浅析JavaScript回调函数应用
2016/05/22 Javascript
三种带箭头提示框总结实例
2016/06/14 Javascript
js利用正则表达式检验输入内容是否为网址
2016/07/05 Javascript
javascript工厂模式和构造函数模式创建对象方法解析
2016/12/30 Javascript
JavaScript创建对象的七种方式(推荐)
2017/06/26 Javascript
JS实现table表格固定表头且表头随横向滚动而滚动
2017/10/26 Javascript
Mac下安装vue
2018/04/11 Javascript
详解vue指令与$nextTick 操作DOM的不同之处
2018/08/02 Javascript
JS大坑之19位数的Number型精度丢失问题详解
2019/04/22 Javascript
在vue中使用回调函数,this调用无效的解决
2020/08/11 Javascript
[01:37]DOTA2超级联赛专访ChuaN 传奇般的电竞之路
2013/06/19 DOTA
python轻松实现代码编码格式转换
2015/03/26 Python
基于Python列表解析(列表推导式)
2018/06/23 Python
Python用于学习重要算法的模块pygorithm实例浅析
2018/08/16 Python
python 实现turtle画图并导出图片格式的文件
2019/12/07 Python
Tensorflow限制CPU个数实例
2020/02/06 Python
详解pandas绘制矩阵散点图(scatter_matrix)的方法
2020/04/23 Python
CSS3的resize属性使用初探
2015/09/27 HTML / CSS
利用 CSS3 实现的无缝轮播功能代码
2017/09/25 HTML / CSS
自定义html标记替换html5新增元素
2008/10/17 HTML / CSS
德国家用电器购物网站:Premiumshop24
2019/08/22 全球购物
白酒市场开发计划书
2014/01/09 职场文书
运动会入场词60字
2014/02/15 职场文书
幼儿园中班区域活动总结
2014/07/09 职场文书
派出所班子党的群众路线对照检查材料思想汇报
2014/10/01 职场文书
幼儿园教研工作总结2015
2015/05/12 职场文书