组合输入状态 定义
组合输入状态 是指在使用输入法(如拼音输入法、日文输入法等)时,用户输入的字符还没有最终确定,处于一个中间状态。这个状态通常发生在用户输入拼音、选择候选词的过程中。让我用一个具体的例子来解释:
组合输入状态的例子
场景:使用拼音输入法输入中文
-
用户输入拼音:
-
用户输入
zhangsan
,此时输入法会显示拼音字母zhangsan
,并等待用户选择候选词。 -
这个阶段就是 组合输入状态。
-
-
用户选择候选词:
-
用户从候选词中选择
张三
,输入法将拼音zhangsan
替换为最终的汉字张三
。 -
这个阶段结束后,组合输入状态结束。
-
组合输入状态的特点
-
输入法行为:
-
在组合输入状态下,输入法会先将拼音字母(或其他中间字符)显示在输入框中。
-
用户选择候选词后,输入法才会将最终字符(如汉字)插入输入框。
-
-
事件触发:
-
compositionstart
:组合输入状态开始时触发。 -
compositionupdate
:组合输入状态更新时触发(如用户继续输入拼音)。 -
compositionend
:组合输入状态结束时触发(如用户选择候选词)。
-
-
输入框内容:
-
在组合输入状态下,输入框的内容是 未确定的中间字符(如拼音字母)。
-
在组合输入状态结束后,输入框的内容是 最终字符(如汉字)。
-
为什么需要处理组合输入状态?
1. 避免干扰输入法
-
在组合输入状态下,输入框的内容是未确定的中间字符(如拼音字母)。
-
如果在这个阶段对输入框内容进行校验或过滤,可能会干扰输入法的正常工作。
-
例如,如果用户在输入拼音时,输入框的内容被强制替换为合法字符,输入法可能无法正确显示候选词。
2. 确保最终内容合法
组合输入状态的兼容性问题
1. 部分浏览器或输入法不支持 compositionstart
和 compositionend
2. 组合输入状态未正确结束
总结
组合输入状态 是指用户在使用输入法时,输入的字符还没有最终确定,处于一个中间状态(如拼音字母)。我们需要通过 compositionstart
和 compositionend
事件来检测组合输入状态,并在组合输入状态结束后对输入框内容进行校验和过滤。这样可以避免干扰输入法的正常工作,同时确保最终内容符合规则。
-
在组合输入状态结束后,输入框的内容是最终字符(如汉字)。
-
此时需要对输入框内容进行校验和过滤,确保内容符合规则。
-
最终代码如下:
-
完整代码1 ==> 以下代码是禁用了用户的粘贴行为,因为此行为会跳过input的校验规则
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Input Filter Example</title> </head> <body> <!-- 第一个输入框(中英文) --> <input type="text" id="input1" placeholder="只能输入中文和英文"> <!-- 第二个输入框(英文和空格) --> <input type="text" id="input2" placeholder="只能输入英文和空格"> <script> // 通用输入处理函数 function createInputFilter(inputEl, regex, replaceRegex) { let isComposing = false; // 是否在输入法组合过程中 let lastValue = ''; // 上次合法值 // 输入法开始 inputEl.addEventListener('compositionstart', () => { isComposing = true; }); // 输入法结束 inputEl.addEventListener('compositionend', () => { isComposing = false; }); // 输入处理 inputEl.addEventListener('input', function(e) { // 如果处于组合输入状态,且输入框内容未变化,则跳过处理 if (isComposing && e.target.value === lastValue) return; // 强制结束组合输入状态(兜底逻辑) isComposing = false; const target = e.target; // 处理输入内容 let newValue = target.value.replace(replaceRegex, ''); // 如果新值不符合规则,则回退到上次合法值 if (!regex.test(newValue)) { newValue = lastValue; } else { lastValue = newValue; } // 更新值 if (target.value !== newValue) { target.value = newValue; } }); // 禁掉粘贴 inputEl.addEventListener('paste', (e) => { e.preventDefault(); // 阻止粘贴 }); } // 第一个输入框处理(中文和英文) const input1 = document.getElementById('input1'); createInputFilter( input1, /^[\u4e00-\u9fa5a-zA-Z]*$/, // 整体校验正则 /[^\u4e00-\u9fa5a-zA-Z]/g // 过滤用正则 ); // 第二个输入框处理(英文和空格) const input2 = document.getElementById('input2'); createInputFilter( input2, /^[a-zA-Z\s]*$/, // 整体校验正则 /[^a-zA-Z\s]/g // 过滤用正则 ); </script> </body> </html>
代码中的组合输入状态处理
在代码中,我们通过
compositionstart
和compositionend
事件来检测组合输入状态:let isComposing = false; // 是否在组合输入状态 // 组合输入开始 inputEl.addEventListener('compositionstart', () => { isComposing = true; }); // 组合输入结束 inputEl.addEventListener('compositionend', () => { isComposing = false; }); // 输入处理 inputEl.addEventListener('input', function(e) { if (isComposing) return; // 如果是组合输入状态,跳过处理 // 校验和过滤逻辑 const target = e.target; let newValue = target.value.replace(replaceRegex, ''); if (!regex.test(newValue)) { newValue = lastValue; } else { lastValue = newValue; } if (target.value !== newValue) { target.value = newValue; } });
完整代码2:以下代码没有禁用用户的复制黏贴行为,因此对复制黏贴和光标做了处理
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Input Filter Example</title> </head> <body> <!-- 第一个输入框(中英文) --> <input type="text" id="input1" placeholder="只能输入中文和英文"> <!-- 第二个输入框(英文和空格) --> <input type="text" id="input2" placeholder="只能输入英文和空格"> <script> // 通用输入处理函数 function createInputFilter(inputEl, regex, replaceRegex) { let isComposing = false; // 是否在输入法组合过程中 let lastValue = ''; // 上次合法值 // 输入法开始 inputEl.addEventListener('compositionstart', () => { isComposing = true; }); // 输入法结束 inputEl.addEventListener('compositionend', () => { isComposing = false; }); // 输入处理 inputEl.addEventListener('input', function(e) { // 如果处于组合输入状态,且输入框内容未变化,则跳过处理 if (isComposing && e.target.value === lastValue) return; // 强制结束组合输入状态(兜底逻辑) isComposing = false; const target = e.target; const selectionStart = target.selectionStart; // 保存光标起始位置 const selectionEnd = target.selectionEnd; // 保存光标结束位置 // 处理输入内容 let newValue = target.value.replace(replaceRegex, ''); // 如果新值不符合规则,则回退到上次合法值 if (!regex.test(newValue)) { newValue = lastValue; } else { lastValue = newValue; } // 更新值 if (target.value !== newValue) { target.value = newValue; // 恢复光标位置 target.setSelectionRange(selectionStart, selectionEnd); } }); // 粘贴处理 inputEl.addEventListener('paste', function(e) { e.preventDefault(); // 阻止默认粘贴行为 const text = (e.clipboardData || window.clipboardData).getData('text'); // 获取粘贴内容 const filtered = text.replace(replaceRegex, ''); // 过滤非法字符 // 插入过滤后的内容 const startPos = inputEl.selectionStart; // 获取光标起始位置 const endPos = inputEl.selectionEnd; // 获取光标结束位置 const currentValue = inputEl.value; // 插入新内容 const newValue = currentValue.slice(0, startPos) + // 光标前的内容 filtered + // 过滤后的粘贴内容 currentValue.slice(endPos); // 光标后的内容 // 更新输入框内容 inputEl.value = newValue; // 恢复光标位置 const newCursorPos = startPos + filtered.length; inputEl.setSelectionRange(newCursorPos, newCursorPos); // 触发 input 事件 const event = new Event('input', { bubbles: true }); inputEl.dispatchEvent(event); }); } // 第一个输入框处理(中文和英文) const input1 = document.getElementById('input1'); createInputFilter( input1, /^[\u4e00-\u9fa5a-zA-Z]*$/, // 整体校验正则 /[^\u4e00-\u9fa5a-zA-Z]/g // 过滤用正则 ); // 第二个输入框处理(英文和空格) const input2 = document.getElementById('input2'); createInputFilter( input2, /^[a-zA-Z\s]*$/, // 整体校验正则 /[^a-zA-Z\s]/g // 过滤用正则 ); </script> </body> </html>
-
组合输入状态的处理逻辑
-
compositionstart
:-
当组合输入状态开始时,将
isComposing
设置为true
。 -
表示当前处于组合输入状态,跳过校验和过滤逻辑。
-
-
compositionend
:-
当组合输入状态结束时,将
isComposing
设置为false
。 -
表示组合输入状态结束,可以执行校验和过滤逻辑。
-
-
input
事件:-
在
input
事件中,如果isComposing
为true
,则跳过处理。 -
如果
isComposing
为false
,则执行校验和过滤逻辑。
-
-
在某些浏览器或输入法下,
compositionstart
和compositionend
可能不会触发。 -
解决方案:
-
在
input
事件中增加兜底逻辑,确保即使compositionend
未触发,也能正确校验输入内容。 -
因为不会触发,
isComposing
不会被设置为true
-
-
用户可能通过其他方式(如直接点击输入框外部)中断组合输入状态,导致
compositionend
未触发。compositionstart
和compositionend
-
解决方案:
-
在
input
事件中检测输入框内容变化,如果内容变化且isComposing
为true
,则强制结束组合输入状态。 -
踩坑
- 我的项目是原生js的老项目,为了绘制UI方便一点,引入了jsx-dom-runtime的库,可以让代码在原生js中使用类似于react的jsx语法,但是这个库比较坑,是一个阉割版的!我绑定onC
ompositionStart
onCompositionEnd
并不会触发回调函数的执行,无奈只能在jsx的组件中又使用原生js获取dom节点绑定
compositionstart
和compositionend!
-