原生js解决在IOS系统自带输入法组合输入时异常的解决方案

​​​​​组合输入状态 定义

组合输入状态 是指在使用输入法(如拼音输入法、日文输入法等)时,用户输入的字符还没有最终确定,处于一个中间状态。这个状态通常发生在用户输入拼音、选择候选词的过程中。让我用一个具体的例子来解释:

组合输入状态的例子

场景:使用拼音输入法输入中文
  1. 用户输入拼音

    • 用户输入 zhangsan,此时输入法会显示拼音字母 zhangsan,并等待用户选择候选词。

    • 这个阶段就是 组合输入状态

  2. 用户选择候选词

    • 用户从候选词中选择 张三,输入法将拼音 zhangsan 替换为最终的汉字 张三

    • 这个阶段结束后,组合输入状态结束。


组合输入状态的特点

  1. 输入法行为

    • 在组合输入状态下,输入法会先将拼音字母(或其他中间字符)显示在输入框中。

    • 用户选择候选词后,输入法才会将最终字符(如汉字)插入输入框。

  2. 事件触发

    • compositionstart:组合输入状态开始时触发。

    • compositionupdate:组合输入状态更新时触发(如用户继续输入拼音)。

    • compositionend:组合输入状态结束时触发(如用户选择候选词)。

  3. 输入框内容

    • 在组合输入状态下,输入框的内容是 未确定的中间字符(如拼音字母)。

    • 在组合输入状态结束后,输入框的内容是 最终字符(如汉字)。


为什么需要处理组合输入状态?

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语法,但是这个库比较坑,是一个阉割版的!我绑定onCompositionStart  onCompositionEnd并不会触发回调函数的执行,无奈只能在jsx的组件中又使用原生js获取dom节点绑定compositionstart 和 compositionend!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值