You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jQuery中role=textbox的div替换Emoji后光标复位问题求助

解决contenteditable div替换Emoji后光标跳转到开头的问题

场景:开发一款即时通讯网页的Electron移植版本,网页原生不支持Emoji功能,因此实现了一套文本解析系统,将配置的Emoji字符串替换为对应Emoji。但遇到问题:每次替换Emoji后,光标会回到div的开头。目标div是设置了role=textbox的contenteditable元素,不希望修改其核心HTML,仅通过后台脚本解决。

原相关代码

JavaScript代码

$(window).on('keyup', function(e) {
  // 如果按下的不是回车键,则继续执行
  if (!(e.key === 'Enter')) {
    // 获取当前聚焦的元素
    let focus = $(":focus");
    // 校验元素类名,确认是目标文本框
    if (focus.attr('class') == "euyIb") {
      // 获取文本框的文本内容
      let text = focus.text();
      // 统计字符串中特殊字符":"的数量
      let count = (text.split(":").length - 1);
      // 如果特殊字符数量不少于2,则假设存在可替换的Emoji
      if (count >= 2) {
        // 创建字符串中包含的Emoji数组
        let contains = [];
        // 遍历所有配置的Emoji,检查是否存在于字符串中
        for (emoji in replaceables) {
          let target = replaceables[emoji];
          if (text.includes(`:${target.name}:`)) {
            contains.push({
              name: `:${target.name}:`,
              emoji: target.emoji
            });
          }
        }

        for (inst in contains) {
          let item = contains[inst];
          $(":focus").text(text.replace(item.name, item.emoji));
          // 此处应将光标重置到末尾
        }
      }
    }
  }
});

HTML结构

<div role="textbox" dir="auto" class="euyIb" contenteditable="true" placeholder="Send a chat" style="caret-color: rgb(242, 60, 87);" id="enter-box">hi 💀</div>

解决方案

核心思路是:替换文本后,通过原生DOM API手动将光标定位到内容末尾,无需修改目标div的核心HTML,仅在现有脚本中添加光标定位逻辑。

步骤1:添加光标定位函数

定义一个用于将光标移动到contenteditable元素末尾的函数:

function moveCursorToEnd(element) {
  const range = document.createRange();
  const selection = window.getSelection();
  range.selectNodeContents(element);
  range.collapse(false); // 折叠到内容末尾
  selection.removeAllRanges();
  selection.addRange(range);
}

步骤2:修改原脚本,替换文本后调用光标定位

调整原替换逻辑,先在内存中完成所有文本替换,再一次性更新DOM并定位光标:

$(window).on('keyup', function(e) {
  if (!(e.key === 'Enter')) {
    let focus = $(":focus");
    if (focus.attr('class') === "euyIb") {
      let text = focus.text();
      let count = (text.split(":").length - 1);
      if (count >= 2) {
        let contains = [];
        for (emoji in replaceables) {
          let target = replaceables[emoji];
          if (text.includes(`:${target.name}:`)) {
            contains.push({
              name: `:${target.name}:`,
              emoji: target.emoji
            });
          }
        }

        // 先在内存中完成所有替换,减少DOM操作次数
        let updatedText = text;
        contains.forEach(item => {
          updatedText = updatedText.replace(item.name, item.emoji);
        });
        focus.text(updatedText);
        
        // 将光标移到内容末尾
        moveCursorToEnd(focus[0]);
      }
    }
  }
});

关键优化点

  • 原代码循环调用text()替换会触发多次DOM重绘,现在改为内存中批量替换后一次性更新,提升性能。
  • 使用原生DOM API操作光标,确保对contenteditable元素的兼容性,无需依赖jQuery的额外方法。

内容的提问来源于stack exchange,提问作者SpecifiesDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 15:15:33