马黑黑 发表于 2026-6-18 17:39

规避输入法编码上屏问题

<style>
    .artBox { font: normal 18px/1.5 sans-serif; overflow: auto; position: relative; }
    .artBox p { margin: 10px 0; }
    .artBox h1, .artBox h2 { margin: 8px 0; }
    .artBox code { background: #f7f4f3; padding: 2px 6px; tab-size: 4; }
    .artBox blockquote { margin: 10px 20px; padding: 2px 15px; border-left: 3px solid skyblue; background: #e7e5e3; }
</style>

<div class="artBox">
    <p>有些Web应用在触发诸如撤销、重做等机制过程中,凡涉及到中文输入法输入的内容,会在编辑器里呈现中文词条的输入法编码。以下案例虽然不是撤销重做,但也可以展现词条编码“上屏”的情形,案例中使用流行通用的防抖机制来限制输入文本的“上屏”操作,具体达成的是:用户的键盘输入会实时上屏,但连续的输入仅在两次输入之间间隔一秒,输入的内容才会“上屏”。点击“预览”进入效果演示页面,请尝试使用中文输入法输入任何文字(单字或词组都可以),故意让输入法候选词窗口停留一小段时间再选择输出的词条,此间会发现,“大屏”文本框里存在文字的输入法编码文本——</p>
    <div class="hEdiv" data-prev="1"><pre class="hEpre">
&lt;style&gt;
    .tMid { text-align: center; margin: 20px; }
    #showbox { width: 600px; height: 200px; padding: 12px; }
    #inputbox { width: 300px; padding: 8px; }
&lt;/style&gt;

&lt;p class="tMid"&gt;
    &lt;textarea id="showbox" value="" readonly&gt;&lt;/textarea&gt;
&lt;/p&gt;
&lt;p class="tMid"&gt;
    &lt;label for="inputbox" onclick="inputbox.value=''"&gt;请输入:&lt;/label&gt;
    &lt;input id="inputbox" type="text" value="" /&gt;
&lt;/p&gt;

&lt;script&gt;
    // 防抖通用函数
    function debounce(func, delay) {
      let timer = null; // 定义定时器
      return function (...args) {
            clearTimeout(timer); // 清除之前的定时器
            timer = setTimeout(() =&gt; {
                func.apply(this, args); // 延迟执行函数
            }, delay);
      };
    }

    // 输入内容搬到“大屏幕”
    inputbox.addEventListener('keyup', debounce((e) =&gt; {
      showbox.value += inputbox.value + '\n';
    }, 1000));
&lt;/script&gt;
    </pre></div>
    <p>本文的任务就是避免上述案例展示的词条编码“上屏”现象的发生。</p>
    <p>这首先需要理解输入法上屏的基本工作流程,分为三个步骤:</p>
    <h2>一、compositionstart(开始合成)</h2>
    <p>以汉字“我”为例,其完整拼音输入法编码是
      <code>wo</code>,当输入第一个字母
      <code>w</code>,合成工作开始,这回触发 compositionstart 流程。
    </p>
    <blockquote>
      <p>
            <strong>JS compositionstart 事件</strong>
      </p>
      <p>compositionstart 事件是在用户开始使用文本合成系统,如输入法编辑器(IME)开始新的文本合成会话时触发的。例如,当用户使用拼音输入法开始输入汉字时,就会触发此事件。这个事件对于处理输入法中的文本输入尤为重要,因为它允许开发者在文本的合成过程开始时执行特定的逻辑。</p>
    </blockquote>
    <h2>二、compositionupdate(合成更新)</h2>
    <p>全拼输入“我”字,下一个字母是
      <code>o</code>,这一部分的输入本质上实在更新编码的合成,触发编码的合成更新事件。
    </p>
    <blockquote>
      <p>
            <strong>JS compositionupdate 事件</strong>
      </p>
      <p>当文本合成系统(如输入法编辑器)控制的文本合成会话中接收到新字符时,会触发 compositionupdate 事件。该事件可能多次触发,例如输入“我们”,
            <code>w</code>之后的
            <code>omen</code>这四个字母都会各自触发一次 compositionupdate 事件。
      </p>
    </blockquote>
    <h2>三、compositionend(合成结束)</h2>
    <p>拼音输入法输入环境下,通常文字的编码打完还不能上屏,需要按空格键或候选序号或以其它方式操作(如鼠标点击)才能让文字上屏;支持顶格上屏的输入法(例如五笔等)如果启用了顶格上屏机制则编码输入完毕会自动上屏。不论手动选择上屏还是自动上屏,上屏动作发生时会触发文本合成结束信息,触发 compositionend 事件。</p>
    <blockquote>
      <p>
            <strong>JS compositionend 事件</strong>
      </p>
      <p>当文本段落的组成完成或取消时,compositionend 事件将被触发 (具有特殊字符的触发,需要一系列键和其他输入,如语音识别或移动中的字词建议)。</p>
    </blockquote>
    <p>知道了输入法上屏的机制,我们就可以找到实现的方法。现在,将上述案例的代码修改如下,我们的目标就可以实现:</p>
    <div class="hEdiv" data-prev="1"><pre class="hEpre">
&lt;style&gt;
    .tMid { text-align: center; margin: 20px; }
    #showbox { width: 600px; height: 200px; padding: 12px; }
    #inputbox { width: 300px; padding: 8px; }
&lt;/style&gt;

&lt;p class="tMid"&gt;
    &lt;textarea id="showbox" value="" readonly&gt;&lt;/textarea&gt;
&lt;/p&gt;
&lt;p class="tMid"&gt;
    &lt;label for="inputbox" onclick="inputbox.value=''"&gt;请输入:&lt;/label&gt;
    &lt;input id="inputbox" type="text" value="" /&gt;
&lt;/p&gt;

&lt;script&gt;
    let composing = false; // 合成状态标记

    function debounce(func, delay) {
      let timer = null; // 定义定时器
      return function (...args) {
            clearTimeout(timer); // 清除之前的定时器
            timer = setTimeout(() =&gt; {
                func.apply(this, args); // 延迟执行函数
            }, delay);
      };
    }

    // 监听输入框键位输入 :内容搬到“大屏幕”
    inputbox.addEventListener('keyup', debounce((e) =&gt; {
      if (!composing) showbox.value += inputbox.value + '\n';
    }, 1000));

    // 监听输入框合成动作开始 : 正在合成,禁止上屏
    inputbox.addEventListener('compositionstart', () =&gt; composing = true);

    // 监听输入框合成动作结束 : 上屏
    inputbox.addEventListener('compositionend', () =&gt; composing = false);
&lt;/script&gt;
    </pre></div>
    <p>核心:使用自定义的 composing 变量来记录输入法输入特定文字的合成态和完成态,开始时值为假(false),这样:其一,英文输入状态下输入的文本正常上大屏;其二,中文输入法状态下,监听 compositionstart(合成开始)事件,只要该事件被触发(开始输入汉字),合成态就存在(
      <code>composing = true</code>),此时会阻止上大屏的操作,同时监听 compositionend 事件,即,当输入法确定选词操作被触发时激活完成态,即令
      <code>composing = false</code>,此时文本才可以上大屏。而 composing 变量必须作为一个开关作用于文本框的输入监听事件(详见案例二第 30 行代码的 if 语句,案例一对应语句即第 29 行代码中没有 if 条件判断)。
    </p>
</div>

<script type="module">
    import hl from 'https://638183.freep.cn/638183/web/js/hl.js';
    const divs = document.querySelectorAll('.hEdiv');
    const pres = document.querySelectorAll('.hEpre');
    divs.forEach((div,idx) => hl.hl(div, pres));
</script>

梦江南 发表于 2026-6-18 18:08

谢谢黑黑老师辛苦,学习了。{:4_187:}

红影 发表于 2026-6-18 19:24

去试了一下,输入后需要停1秒就上屏了,如果一直输入就可以是很长一段文字。

红影 发表于 2026-6-18 19:29

不仅是输入的,复制过去的文字停1秒也能上屏呢。
黑黑讲解辛苦了{:4_190:}

马黑黑 发表于 2026-6-19 21:39

红影 发表于 2026-6-18 19:29
不仅是输入的,复制过去的文字停1秒也能上屏呢。
黑黑讲解辛苦了

核心问题不是这个

马黑黑 发表于 2026-6-19 21:39

红影 发表于 2026-6-18 19:24
去试了一下,输入后需要停1秒就上屏了,如果一直输入就可以是很长一段文字。

这事防抖机制,不是本文的核心内容

马黑黑 发表于 2026-6-19 21:39

梦江南 发表于 2026-6-18 18:08
谢谢黑黑老师辛苦,学习了。

{:4_180:}

红影 发表于 2026-6-20 22:40

马黑黑 发表于 2026-6-19 21:39
核心问题不是这个

嗯,主要是学习如何上屏的事。

红影 发表于 2026-6-20 22:41

马黑黑 发表于 2026-6-19 21:39
这事防抖机制,不是本文的核心内容

有很多事都以为是天然的,实际不是,比如输入后的空格上屏,以为天生就是这样的。

马黑黑 发表于 2026-6-20 22:58

红影 发表于 2026-6-20 22:41
有很多事都以为是天然的,实际不是,比如输入后的空格上屏,以为天生就是这样的。

所以很多东东是不能想当然的

马黑黑 发表于 2026-6-20 22:58

红影 发表于 2026-6-20 22:40
嗯,主要是学习如何上屏的事。

酱紫

红影 发表于 2026-6-21 20:04

马黑黑 发表于 2026-6-20 22:58
所以很多东东是不能想当然的

一直使用的东西总是不知不觉就认为是天生的{:4_173:}

马黑黑 发表于 2026-6-22 19:32

红影 发表于 2026-6-21 20:04
一直使用的东西总是不知不觉就认为是天生的

这是地道的想当然

红影 发表于 2026-6-24 13:31

马黑黑 发表于 2026-6-22 19:32
这是地道的想当然

人总有很多想当然的时候啊{:4_173:}

马黑黑 发表于 2026-6-24 22:37

红影 发表于 2026-6-24 13:31
人总有很多想当然的时候啊

那倒也是

红影 发表于 2026-6-25 20:57

马黑黑 发表于 2026-6-24 22:37
那倒也是

不能够意识到是想当然,就比一直想当然的好{:4_173:}

马黑黑 发表于 2026-6-26 20:40

红影 发表于 2026-6-25 20:57
不能够意识到是想当然,就比一直想当然的好

好那么一丁点儿

红影 发表于 2026-6-28 17:30

马黑黑 发表于 2026-6-26 20:40
好那么一丁点儿

好一点也是好{:4_173:}

马黑黑 发表于 2026-6-28 22:18

红影 发表于 2026-6-28 17:30
好一点也是好

那是,不好也好

红影 发表于 2026-6-28 22:52

马黑黑 发表于 2026-6-28 22:18
那是,不好也好

能好着为什么要不好啊。
页: [1] 2 3
查看完整版本: 规避输入法编码上屏问题