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

React中contentEditable的div调用setState后光标跳转到末尾如何解决

React可编辑div光标跳转问题修复方案

问题根因

你遇到的光标自动跳转到内容末尾的核心原因是:在setContentValue方法中手动对contenteditable元素重新赋值了innerText,这一操作会让浏览器判定元素内容被完全重写,自动重置光标位置到内容末尾。同时你原代码中还漏了ref的初始化逻辑,也可能导致潜在的引用异常。

修复方案

方案1:仅需要同步输入内容到state(无内容修改需求)

如果你只是需要把输入内容同步到state用于下方展示,完全不需要手动修改div的innerText,contenteditable元素会自动响应用户输入更新内容,直接删除多余的赋值行即可解决问题:

// 构造函数补充ref初始化
constructor(props) {
  super(props);
  this.state = {contentValue: ''};
  this.inputRef = React.createRef(); // 补充这行
}

// 原setContentValue修改后
setContentValue = (e) => {
  // some condition check
  // 删除这行多余代码:this.inputRef.current.innerText = e.target.innerText;
  this.setState({contentValue: e.target.innerText})
}

方案2:有内容过滤/格式化需求,需要手动修改输入内容

如果你需要对用户输入做拦截修改(比如敏感词过滤、格式转换),就需要在修改内容前保存光标位置,修改完成后手动恢复光标:

export default class MyEditor extends React.Component {      
  constructor(props) {
    super(props);
    this.state = {contentValue: ''};
    this.inputRef = React.createRef();
  }

  // 获取当前光标偏移量
  getCaretPosition = () => {
    const selection = window.getSelection();
    if (selection.rangeCount === 0) return 0;
    const range = selection.getRangeAt(0);
    const preCaretRange = range.cloneRange();
    preCaretRange.selectNodeContents(this.inputRef.current);
    preCaretRange.setEnd(range.endContainer, range.endOffset);
    return preCaretRange.toString().length;
  }

  // 设置光标到指定偏移量
  setCaretPosition = (offset) => {
    const selection = window.getSelection();
    const range = document.createRange();
    const textNode = this.inputRef.current.firstChild;
    if (!textNode) return;
    range.setStart(textNode, Math.min(offset, textNode.length));
    range.collapse(true);
    selection.removeAllRanges();
    selection.addRange(range);
  }

  onKeyDown = (e) => {
    if (e.keyCode == 13) { 
      e.preventDefault();
      return;
    }
  }

  setContentValue = (e) => {
    // 先保存当前光标位置
    const caretOffset = this.getCaretPosition();
    // 这里写你的自定义内容处理逻辑,比如输入过滤、格式化
    const newContent = e.target.innerText;
    // 手动更新内容后恢复光标
    this.inputRef.current.innerText = newContent;
    this.setCaretPosition(caretOffset);
    this.setState({contentValue: newContent})
  }

  render(){
    return(
      <>
        <div className="mydiv"
             contentEditable="true"
             placeholder="Enter text"
             suppressContentEditableWarning={true}
             onInput={(e) => this.setContentValue(e)}
             ref={this.inputRef}
             onKeyDown={this.onKeyDown}>
        </div>
        <div>
           {this.state.contentValue}  
        </div>
      </>
    )
  }
}

注意:如果你的可编辑div中包含富文本元素(不止纯文本),需要调整光标计算逻辑适配节点结构,上述代码适配纯文本场景的可编辑需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 23:06:01