h5 android数字键盘,【笔记】移动端H5数字键盘input type=number的处理(IOS和Android)...

在Vue中的项目,基于VUX-UI开发,一个常见的需求:

1、金额输入框

2、弹出数字键盘

3、仅支持输入两位小数,限制最大11位数,不允许0开头

后续:与UI沟通后, 思路调整为限制输入,并减少正则替换输入值出现的闪动。后续改动如下,注意点如下:

1、处理思路

A。在用户输入的键盘事件中,对于不符合的输入,阻止默认行为和事件冒泡。

不符合输入的规则如下:

1)当前输入框中的长度大于等于配置的max

2)非数字和小数点

3)当前输入框中已存在小数点,或第一位输入小数点

B。在获取值后,对于不符合两位小数的值,用watch正则替换后,再下一次渲染(会出现先12.000到12.00的闪动)

2、阻止键盘事件在哪个阶段?

keypress。

因为keydown和keyup得到的是keyEvent中键值是原始的组合键值,需要判断不同环境和浏览器对keycode的实现不同以及是否有shift/alt等。比如在IOS中keydown,对于字符$ @,keycode都是0;中文键盘和英文键盘中的数字keycode不一致。

而kepress得到的是组合解析后的实际值,android和ios大部分表现一致。

3、Android的数字键盘中的小数点的特殊处理

调试发现,安卓的数字键盘中,小数点做了特殊处理:

1)无法捕获到keypress事件

2)keydown事件中keEvent的keycode是0,无法用于判断

3)keydown事件中keEvent的keyIdentifier === 'U+0000'

4)在keydown事件以及keyuup或其它事件中, 用preventDefault和stopPropagation阻止默认行为和事件冒泡,不能阻止input框输入小数点.

所以对这个问题处理,只能沿用之前用在watch中处理空值问题的思路。

4、最终效果

IOS中默认拉起含特殊字符的数字键盘,对于非法输入不会出现任何闪动,对于长度越界的会出现闪动

Andriod中默认拉起九宫格数字键盘,没有特殊字符,小数点会出现闪动,对于长度越界的会出现闪动

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

:title="title":max="currentMax":min="currentMin":type="type"v-model="currentValue"@on-focus="onFoucus()"@on-blur="onBlur()":show-clear="showClear":placeholder="placeholder"ref="xinput">

data() {return{

currentValue:this.value,

};

},

computed: {

currentMax() {return (this.type === 'number') ? undefined : this.max;

},

currentMin() {return (this.type === 'number') ? undefined : this.min;

}

},

props: {

title: String,

max: Number,

min: Number,

type: String,

showClear: {

type: Boolean,default: true,

},

placeholder: String,

value: [String, Number],

filter: {

type: Function,default: (value) =>{

let formattedValue= '';

const match= value.match(/^([1-9]d*(.[d]{0,2})?|0(.[d]{0,2})?)[d.]*/);if(match) {

formattedValue= match[1];

}returnformattedValue;

},

}

},

watch: {

currentValue(val, oldVal) {//调用filter过滤数据

let formattedValue = this.filter(val);if (this.type === 'number') {

formattedValue= this.typeNumberFilter(formattedValue, oldVal);

}if (val !== formattedValue || val === '') {

setTimeout(()=>{this.currentValue =formattedValue;

},0);

}this.$emit('input', formattedValue);

},

value(value) {this.currentValue =value;

},

},

methods: {

blur() {this.$refs.xinput.blur();

},

focus() {this.$refs.xinput.focus();

},

onFoucus() {this.$emit('on-focus');

},

onBlur() {this.$emit('on-blur');

},

typeNumberFilter(val, oldVal) {

const inputEle= this.$refs.xinput.$refs.input;

let formattedValue=val;//TODO: 待大范围验证:Android处理连续输入..后,type=number的input框会把值修改为'',这里手动替换为上次的currentValue

//问题描述: 1.00. 不会触发值改变,1.00.不会触发值改变,1.00.【d.】都会把值修改为空字符串''。hack处理的条件说明如下:

//1、输入框拿到的是空值(因input=number导致输入框立即被赋予空值。点击清除按钮时,这里input输入框还是上次的值)

//2、上次输入值有效

if (inputEle.value === '' && oldVal && oldVal.match(/^(d)[d.]+/)) {

formattedValue=oldVal;

}returnformattedValue;

},

isBackspace(keyCode) {return keyCode === 8;

},

isDot(keyCode) {return keyCode === 46 || keyCode === 190;

},

isNumber(keyCode) {return (keyCode >= 48 && keyCode <= 57);

},

isNotNumberKeycode(keyCode) {return !this.isBackspace(keyCode) && !this.isDot(keyCode) && !this.isNumber(keyCode);

},

isDotStart(keyCode, inputVal) {return this.isDot(keyCode) && (!inputVal || inputVal === '' || /./.test(inputVal));

},

isFinalInput(inputVal) {return inputVal.length >= this.max;

}

},

mounted() {if (this.type === 'number') {

const inputEle= this.$refs.xinput.$refs.input;

inputEle.οnkeydοwn= (e) =>{//Android小数点特殊处理

const inputVal =inputEle.value;if (e.keyIdentifier === 'U+0000' && (!inputVal || inputVal === '')) {

inputEle.value= '';

}

};//eslint-disable-next-line

inputEle.onkeypress = (e) =>{

const keyCode=e.keyCode;

const inputVal=inputEle.value;if (this.isNotNumberKeycode(keyCode) || this.isDotStart(keyCode, inputVal)|| this.isFinalInput(inputVal)) {

e.preventDefault();

e.stopPropagation();return false;

}

};

}

}

};

View Code

第一,首先想到额就是在VUX-UI中制定type=number。--不可行

VUX中的文档和代码说明,type=number不支持maxLength,会报错,而且没有正则替换的处理或者钩子函数,只有输入后提示校验信息。

第二,基于VUX中XInput封装,有如下问题

1)两层v-model,正则替换的值不会触发input框渲染

解决:currentValue赋值为foramttedValue,放入setTimeout(func ,0)中,让input框先渲染为正则替换前的值,再渲染为替换后的值

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

currentValue(val, oldVal) {//调用filter过滤数据

let formattedValue = this.filter(val);if (this.type === 'number') {

formattedValue= this.typeNumberFilter(formattedValue, oldVal);

}if (val !== formattedValue || val === '') {

setTimeout(()=>{this.currentValue =formattedValue;

},0);

}this.$emit('input', formattedValue);

},

View Code

2)数字键盘input type=number,会导致maxlength失效,无法限制长度

解决:用slice(0, max)处理

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

if (formattedValue.length > this.max) {

formattedValue= formattedValue.slice(0, this.max);

}

View Code

3)数字键盘input type=number ,连续输入小数点...导致实际值和显示值不一致

解决:用原生的 inputElement.value = oldValue处理

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

const inputEle = this.$children[0].$refs.input;//TODO: 待大范围验证:处理连续输入..后,type=number的input框会把值修改为''的问题;fastclick导致type=number报错

//问题描述: 1.00. 不会触发值改变,1.00.不会触发值改变,1.00.【d.】都会把值修改为空字符串''。hack处理的条件说明如下:

//1、当校验后是空值,(因input=number,formattedValue为''表明 原始newVal也为'')

//2、输入框拿到的是空值(因input=number导致输入框立即被赋予空值。点击清除按钮时,这里input输入框还是上次的值)

//3、上次输入大于两位(避免最后一位无法删除的问题。最后一位删除时,oldVal.length === 1)

if (formattedValue === '' && inputEle.value === '' && oldVal && oldVal.match(/^(d)[d.]+/)) {

formattedValue=oldVal;

}

setTimeout(()=>{

inputEle.value=formattedValue;

},0);

View Code

4)IOS中数字键盘有%$*等特殊字符

解决:用原生的 inputElement.onkeydown监听事件,非数字和退格和小数点直接return事件

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

mounted() {if (this.type === 'number') {

const inputEle= this.$refs.xinput.$refs.input;//eslint-disable-next-line

inputEle.onkeydown = (e) =>{

const keyCode=e.keyCode;if (!this.isBackspace(keyCode) && !this.isDot(keyCode) && !this.isNumber(keyCode)) {//其他按键

e.preventDefault();

e.stopPropagation();return false;

}

};

}

}

View Code

第三,其他说明

为什么不用 type=tel?

type=tel在ios中没有小数点

第四,全部代码

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

:title="title":max="currentMax":min="currentMin":type="type"v-model="currentValue"@on-focus="onFoucus()"@on-blur="onBlur()":show-clear="showClear":placeholder="placeholder"ref="xinput">

data() {return{

currentValue:this.value,

};

},

computed: {

currentMax() {return (this.type === 'number') ? undefined : this.max;

},

currentMin() {return (this.type === 'number') ? undefined : this.min;

}

},

props: {

title: String,

max: Number,

min: Number,

type: String,

showClear: {

type: Boolean,default: true,

},

placeholder: String,

value: [String, Number],

filter: {

type: Function,default: (value) =>{

let formattedValue= '';

const match= value.match(/^([1-9]d*(.[d]{0,2})?|0(.[d]{0,2})?)[d.]*/);if(match) {

formattedValue= match[1];

}returnformattedValue;

},

}

},

watch: {

currentValue(val, oldVal) {//调用filter过滤数据

let formattedValue = this.filter(val);if (this.type === 'number') {

formattedValue= this.typeNumberFilter(formattedValue, oldVal);

}if (val !== formattedValue || val === '') {

setTimeout(()=>{this.currentValue =formattedValue;

},0);

}this.$emit('input', formattedValue);

},

value(value) {this.currentValue =value;

},

},

methods: {

onFoucus() {this.$emit('on-focus');

},

onBlur() {this.$emit('on-blur');

},

typeNumberFilter(val, oldVal) {

const inputEle= this.$refs.xinput.$refs.input;

let formattedValue=val;//由于type=number不支持maxLength,用slice模拟

if (formattedValue.length > this.max) {

formattedValue= formattedValue.slice(0, this.max);

}//TODO: 待大范围验证:处理连续输入..后,type=number的input框会把值修改为''的问题;fastclick导致type=number报错

//问题描述: 1.00. 不会触发值改变,1.00.不会触发值改变,1.00.【d.】都会把值修改为空字符串''。hack处理的条件说明如下:

//1、当校验后是空值,(因input=number,formattedValue为''表明 原始newVal也为'')

//2、输入框拿到的是空值(因input=number导致输入框立即被赋予空值。点击清除按钮时,这里input输入框还是上次的值)

//3、上次输入大于两位(避免最后一位无法删除的问题。最后一位删除时,oldVal.length === 1)

if (formattedValue === '' && inputEle.value === '' && oldVal && oldVal.match(/^(d)[d.]+/)) {

formattedValue=oldVal;

}

setTimeout(()=>{

inputEle.value=formattedValue;

},0);returnformattedValue;

},

isBackspace(keyCode) {return keyCode === 8;

},

isDot(keyCode) {return keyCode === 46 || keyCode === 110 || keyCode === 190;

},

isNumber(keyCode) {return (keyCode >= 48 && keyCode <= 57) || (keyCode >= 96 && keyCode <= 105);

},

},

mounted() {if (this.type === 'number') {

const inputEle= this.$refs.xinput.$refs.input;//eslint-disable-next-line

inputEle.onkeydown = (e) =>{

const keyCode=e.keyCode;if (!this.isBackspace(keyCode) && !this.isDot(keyCode) && !this.isNumber(keyCode)) {//其他按键

e.preventDefault();

e.stopPropagation();return false;

}

};

}

}

};

View Code

你可能感兴趣的:(h5,android数字键盘)