issues-blog
issues-blog copied to clipboard
input 的 compositionstart 和 compositionend 事件(转)
--转自饿了么
在 Web 开发中,经常要对表单元素的输入进行限制,比如说不允许输入特殊字符,标点。通常我们会监听 input 事件:
inputElement.addEventListener('input', function(event) {
let regex = /[^1-9a-zA-Z]/g;
event.target.value = event.target.value.replace(regex, '');
event.returnValue = false
});
这段代码在 Android 上是没有问题的,但是在 iOS 中,input 事件会截断非直接输入,什么是非直接输入呢,在我们输入汉字的时候,比如说「喜茶」,中间过程中会输入拼音,每次输入一个字母都会触发 input 事件,然而在没有点选候选字或者点击「选定」按钮前,都属于非直接输入。
这显然不是我们想要的结果,我们希望在直接输入之后才触发 input 事件,这就需要引出我要说的两个事件—— compositionstart 和compositionend。
compositionstart 事件在用户开始进行非直接输入的时候触发,而在非直接输入结束,也即用户点选候选词或者点击「选定」按钮之后,会触发 compositionend 事件。
var inputLock = false;
function do(inputElement) {
var regex = /[^1-9a-zA-Z]/g;
inputElement.value = inputElement.value.replace(regex, '');
}
inputElement.addEventListener('compositionstart', function() {
inputLock = true;
});
inputElement.addEventListener('compositionend', function(event) {
inputLock = false;
do(event.target);
})
inputElement.addEventListener('input', function(event) {
if (!inputLock) {
do(event.target);
event.returnValue = false;
}
});
添加一个 inputLock 变量,当用户未完成直接输入前,inputLock 为 true,不触发 input 事件中的逻辑,当用户完成有效输入之后,inputLock 设置为 false,触发 input 事件的逻辑。这里需要注意的一点是,compositionend 事件是在 input 事件后触发的,所以在 compositionend事件触发时,也要调用 input 事件处理逻辑。
这不是刚出生就有的麽

mark
mark
类似与vue指令v-model 的封装,flag绑定在dom节点属性上
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>inputEvent</title>
</head>
<body>
show: <span id="show"></span>
<div>
<input id="inp" type="text">
</div>
<script>
inp.addEventListener('input', function (e) {
if(e.target.composing) return
show.innerHTML = e.target.value
})
inp.addEventListener('compositionstart', function (e) {
e.target.composing = true
})
inp.addEventListener('compositionend', function (e) {
if(!e.target.composing) return
e.target.composing = false
trigger(e.target, 'input')
})
function trigger(el, type) {
var e = document.createEvent('HTMLEvents')
e.initEvent(type, true, true)
el.dispatchEvent(e)
}
</script>
</body>
</html>
@julytian 最近我在做项目遇到类似的问题,代码差不多:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input id='input' type ='text' />
</body>
<script>
window.onload = () => {
const input = document.getElementById('input');
let isCompsition = false;
input.onchange = e => {
console.log(e.target.value);
};
input.oninput = e => {
const text = e.target.value;
console.log(text);
if (!isCompsition) {
input.value = input.value.replace(/[^\u4e00-\u9fa50-9a-zA-Z ]/, '');
e.returnValue = false;
}
};
input.addEventListener('compositionstart', () => {
isCompsition = true;
});
input.addEventListener('compositionend', () => {
isCompsition = false;
// 只能中文,数字,英文,空格
input.value = input.value.replace(/[^\u4e00-\u9fa50-9a-zA-Z ]/, '');
});
};
</script>
</html>
一切看起来很好,但是ios有一个九宫格的多符号公用键问题,或者全键盘上的‘-’(x键下滑),就会有奇妙的bug。 1)比如我先输入一个单词‘测试’,然后x键下滑输入’-‘,这时什么都ok。正则表达式会删掉‘-’并保留‘测试’。 2)我再次以同样的方式输入’-‘,这个时候理论上会得到跟1)同样的结果。但实际上不是,得到的结果是**‘测’**,‘试’被系统删掉了! 3)再用其他输入做测试,发现只要连输两次‘-’,之前的最后一个字符一定会被删除。 推测如下: ios对于连续输入的两个‘-’会有替换前一个‘-’为‘—’的操作(系统行为),第二次输入‘-’实则分为两步:第一步删掉之前的‘-’(恰好是输入的最后一个字符),第二步替换为‘—’。 所以如果在input事件触发的时候发生正则替换,比如我第一次输入‘-’,第二次输入‘-’时,第一次的已经被正则删掉了,所以系统会默认抓最后一个字符删掉,这就导致了‘-’之前的字符没了,然后第二次输入的‘-’也会被正则干掉,结果就是不但输入没有保持不变,之前输入的最后一个字符也没了。 实测九宫格多符号的选择键也是一样的效果。 解决方案: 1)采用keycode的方式避开某些按键(keydown事件判断),实测不行,ios存在九宫格符号键的keycode和数字键2,3的keycode重叠的问题,一旦采用意味着数字2,3无法输入,否决。 2)最后的方案,回避。即input,change事件不干涉input输入的问题。输入不支持的字符采用提示信息报错。 对此我暂时没有其他特别好的正面干预输入的解决方案,如果有欢迎告知,谢谢。