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

contenteditable编辑器修改innerHTML渲染markdown时如何保留光标位置

光标重置问题原因

你每次更新编辑器内容时直接重写了整个div的innerHTML,会触发容器内DOM树全量重绘,原有光标关联的DOM节点被销毁,浏览器只能将光标重置到容器起始位置。仅将光标固定到末尾的方案无法支持中间位置编辑的场景。

解决思路

通过 Selection + Range API 实现光标位置的记忆和还原:

  1. 触发输入事件后、更新DOM前,计算当前光标在编辑器纯文本内容中的偏移量
  2. 执行Markdown渲染更新内容后,根据记录的偏移量重新定位光标到对应位置
完整实现代码

HTML

<div contenteditable></div>

CSS

div {
  height: 100vh;
  width: 100vw;
  padding: 16px;
  box-sizing: border-box;
  outline: none;
}

JS

const editor = document.querySelector('div');
editor.innerHTML = parse('**dlob**  *cilati*');

editor.addEventListener('input', () => {
  // 记录当前光标在纯文本中的偏移量
  const caretOffset = getTextCaretOffset(editor);
  // 渲染Markdown语法
  editor.innerHTML = parse(editor.innerText);
  // 还原光标位置
  setCaretByTextOffset(editor, caretOffset);
});

function parse(text) {
  return text
    // 替换为非贪婪匹配避免多个同格式串被合并
    .replace(/\*\*(.*?)\*\*/g, '**<strong>$1</strong>**')     // 加粗
    .replace(/\*(.*?)\*/g, '*<em>$1</em>*');                  // 斜体
}

// 获取光标在编辑器纯文本内容中的偏移量
function getTextCaretOffset(editor) {
  const selection = window.getSelection();
  if (selection.rangeCount === 0) return 0;
  
  const range = selection.getRangeAt(0);
  const preRange = document.createRange();
  preRange.selectNodeContents(editor);
  preRange.setEnd(range.endContainer, range.endOffset);
  
  return preRange.toString().length;
}

// 根据纯文本偏移量设置光标位置
function setCaretByTextOffset(editor, offset) {
  const selection = window.getSelection();
  const range = document.createRange();
  range.selectNodeContents(editor);
  
  let currentOffset = 0;
  // 遍历节点找到偏移量对应的文本节点位置
  const traverseNode = (node) => {
    if (node.nodeType === Node.TEXT_NODE) {
      const textLen = node.length;
      if (currentOffset + textLen >= offset) {
        range.setStart(node, offset - currentOffset);
        range.collapse(true);
        return true;
      }
      currentOffset += textLen;
    } else {
      for (const child of node.childNodes) {
        if (traverseNode(child)) return true;
      }
    }
    return false;
  };
  
  traverseNode(editor);
  selection.removeAllRanges();
  selection.addRange(range);
}
可选优化

如果要进一步提升性能和稳定性,可以参考Quill等编辑器的增量更新逻辑:不直接全量替换innerHTML,而是先对比渲染前后的DOM差异,只修改发生变化的节点,减少重绘范围,也能避免极端场景下的光标偏移问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 23:45:08