我们经常会要求用户在文本框中输入特定的数据,或者输入特定格式的数据.例如,必须包含木屑字符,或者必须匹配某种模式.由于文本框在默认情况下没有提供多少验证数据的手段,因此必须是哟个JavaScript来完成此类过滤输入的操作.而综合运用事件和DOM手段,就可以将普通的文本框转换成能够理解用户输入数据的工程型控件.
1.原生事件方法:
- input事件操作el.value值
此处按照事件的顺序均可实现
<!--onkeydown-->
<input onkeydown="this.value=this.value.replace(/\D/g,'')">
<!--onkeypress-->
<input onkeypress="this.value=this.value.replace(/\D/g,'')">
<!--推荐-->
<!--onkeyup-->
<input onkeyup="this.value=this.value.replace(/\D/g,'')">
onkeydown
,onkeypress
都有一个比较明显的缺点,就是非数组字符仍可以输入,只是在下一次输入之后才会替换掉.
keyup
事件能在输入不正确的字符之后立刻就能清除掉, 但是这个动作还是人眼可见的.
封装 : 此种方式也可先定义一个函数,将this
传递到该函数然后进行复杂操作
<input onkeyup="keyUp(this)">
<script>
function keyUp(el) {
el.value = el.value.replace(/\D/g, '');
}
</script>
- 最佳方案
event.preventDefault
为了让不希望输入的字符不会在input框中闪现,可以通过阻止某个按键的默认行为来屏蔽此类字符.在极端的情况下,可以通过下列代码屏蔽所有按键操作.
EventUtil.addHandler(textBox,"keypress",function(event){
event=EventUtil.getEvent(event);
EventUtil.preventDefault(event);
})
放上用到的EventUtil
公共方法
let EventUtil = {
addHandler: function (element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, hander);
} else {
element["on" + type] = handler;
}
},
removeHandler: function (element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
getEvent: function (event) {
return event ? event : window.event;
},
getTarget: function (event) {
return event.target || event.srcElement;
},
preventDefault: function (event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
stopPropagation: function (event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
},
getCharCode: function (event) {
if (typeof event.charCode === "number") {
return event.charCode;
} else {
return event.keyCode;
}
},
getClipboardText: function (event) {
var clipboardData = (event.clipboardData || window.clipboardData);
return clipboardData.getData("text");
},
setClipboardText: function (event, value) {
if (event.clipboardData) {
return event.clipboardData.setData("text/plain", value);
} else if (window.clipboardData) {
return window.clipboardData.setData("text", value);
}
}
}
运行以上代码后,由于所有按键操作都被屏蔽,结果会导致文本框变成只读的.如果只想屏蔽特定的字符,则需要检测keypress事件对应的字符编码,然后再决定如何响应.例如,下列代码只允许用户输入数值.
<input type="text" id="textBox">
<script>
EventUtil.addHandler(textBox, "keypress", function (event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharCode(event);
if (!/\d/.test(String.fromCharCode(charCode)) && charCode>9 && !event.ctrlKey) {
EventUtil.preventDefault(event);
}
})
</script>
例子中,使用EventUtil.getCharCode()实现了跨浏览器取得字符编码,然后使用String.fromCharCode()将字符编码转换成字符串,再使用正则表达式/\d/来测试该字符串,将测试失败的数值使用EventUtil.preventDefault()屏蔽按键事件.
- vue指令方式
通过vue指令可以在input 标签上添加相应指令名称就可以达到操作原生input事件,满足各种input功能需求, 逻辑即清晰亦是解耦,可移植性强.
- 限制input输入数字最大最小值
下面是我封装后的完整示例:
HTML部分
<div id="app">
<h2>input 控制输入</h2>
<div>
<input type="number" v-model.lazy="value" v-int-num="{cb:showAlert,min:10,max:100,decimal:0}" >
</div>
</div>
JavaScript部分
// 截取精确小数方法
const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);
Vue.directive('int-num', {
bind: function (el, binding, vnode) {
let {
cb,//回调函数,可在不满足条件的情况下做一下后续提示等操作
min,//最小值
max,//最大值
decimal //精确小数位数
} = binding.value ? binding.value : {
cb: Function,
min: 0,
max: 100,
decimal: 0
};
if (min > max) {
throw new Error("最小值不能大于最大值");
return;
}
el.keypressHandler = function (event) {
// 当小数位数位0时,此时不允许输入'.'点符号
if (decimal === 0 && event.charCode === 46) {
event.preventDefault();//组织默认行为
event.stopPropagation();//停止传递冒泡事件,不会进入input事件中
}
}
el.inputHandler = function (event) {
if (isNaN(Number(el.value))) {
el.value = "";
} else {
el.value = round(Number(el.value), decimal);
}
cb.call(null, Number(el.value));
if (el.value > max) {
el.value = max;
}
}
el.blurHandler = function () {
if (el.value > max) {
cb.call(this, "最大值是 " + max)
}
if (el.value < min) {
cb.call(this, "最小值是 " + min);
}
}
// 添加事件监听
el.addEventListener("keypress", el.keypressHandler)
el.addEventListener('input', el.inputHandler);
el.addEventListener("blur", el.blurHandler);
},
unbind: function (el) {
// 移除事件监听
el.removeEventListener("keypress", el.keypressHandler)
el.removeEventListener('input', el.inputHandler);
el.removeEventListener("blur", el.blurHandler);
}
});
new Vue({
el: "#app",
data: {
value: ""
},
methods: {
showAlert(e) {
console.info(JSON.stringify(e));
}
}
})
特别注意:
v-model
值绑定的时候需要使用.lazy
进行修饰,因为v-model
内部也是用input
事件截取值和绑定值的,哪个先执行是不确定的,偶尔会看到input值闪动出现。当用.lazy
修饰之后,v-model
内部使用change
事件绑定值,而change事件是在input事件之后,故此时已经是经过自定义处理之后的值,不会出现数值跳动的情况。
其实内部实现也是input事件的原生方法,经过vue指令的封装,控制输入行为会更灵活,也更直观和方便.
如果需要对特定字符进行组织或只允许输入特定字符,可以在keypress事件中用 event.charCode
判断即可实现.
题外话:
为了只允许输入数字可以用input[type=number]和input[type=tel],但是有一些缺陷
- input[type=number]
不支持maxlength,支持输入e
,.
- input[type=tel]
在移动设备上,input[type=tel] 是支持maxlength的,而且只能输入数字键盘。支持输入#
,*
,+
网友评论