issues-blog icon indicating copy to clipboard operation
issues-blog copied to clipboard

input 的 compositionstart 和 compositionend 事件(转)

Open julytian opened this issue 8 years ago • 5 comments

--转自饿了么

在 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 事件处理逻辑。

julytian avatar May 03 '17 07:05 julytian

这不是刚出生就有的麽

image

lcoder avatar Jan 23 '19 02:01 lcoder

mark

saint3347 avatar May 27 '19 06:05 saint3347

mark

Zeus-Iqd avatar Sep 02 '19 13:09 Zeus-Iqd

类似与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>

yoonasy avatar Dec 14 '19 07:12 yoonasy

@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输入的问题。输入不支持的字符采用提示信息报错。 对此我暂时没有其他特别好的正面干预输入的解决方案,如果有欢迎告知,谢谢。

bright-wang avatar May 18 '20 10:05 bright-wang