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

React中contentEditable Div输入时光标跳至开头问题求解

React contentEditable 光标跳转、输入反向问题解决方法

问题根源

你当前的实现存在两个核心问题:

  1. 每次输入触发onInput后,setContent更新state,React会重新渲染div,dangerouslySetInnerHTML会完全替换div内容,导致光标被强制重置到开头。
  2. React的state更新是异步的,加上dangerouslySetInnerHTML的渲染时机问题,会导致新输入的内容被错误追加到旧内容前,出现反向显示的情况。

可行解决方案

方案一:放弃dangerouslySetInnerHTML,用ref同步内容(推荐)

让contentEditable的div自然维护DOM内容,state仅做同步备份,避免强制替换内容导致的光标问题:

import { useState, useRef } from 'react';

function Editor() {
  const [content, setContent] = useState('');
  const editorRef = useRef(null);

  const handleInput = () => {
    if (editorRef.current) {
      const currentContent = editorRef.current.innerHTML;
      setContent(currentContent);
      // 可在此处添加内容过滤、格式处理逻辑
    }
  };

  // 如需初始化内容,用useEffect设置
  // useEffect(() => {
  //   if (editorRef.current) {
  //     editorRef.current.innerHTML = '<p>初始内容</p>';
  //   }
  // }, []);

  return (
    <div
      ref={editorRef}
      contentEditable="true"
      onInput={handleInput}
      className='Editor'
    />
  );
}

方案二:手动保存/恢复光标位置(适用于必须用dangerouslySetInnerHTML的场景)

如果需要严格通过state控制编辑器内容,可在更新前保存光标位置,渲染完成后恢复:

import { useState, useEffect, useRef } from 'react';

function Editor() {
  const [content, setContent] = useState('');
  const editorRef = useRef(null);
  const cursorRangeRef = useRef(null);

  // 保存当前光标位置
  const saveCursor = () => {
    const selection = window.getSelection();
    if (selection.rangeCount > 0) {
      cursorRangeRef.current = selection.getRangeAt(0);
    }
  };

  // 恢复光标位置
  const restoreCursor = () => {
    const selection = window.getSelection();
    if (cursorRangeRef.current) {
      selection.removeAllRanges();
      selection.addRange(cursorRangeRef.current);
    }
  };

  const handleInput = (e) => {
    e.preventDefault();
    saveCursor(); // 先保存光标再更新内容
    const val = e.target.innerHTML;
    setContent(val);
  };

  // 内容更新后恢复光标
  useEffect(() => {
    if (editorRef.current) {
      restoreCursor();
    }
  }, [content]);

  return (
    <div
      ref={editorRef}
      contentEditable="true"
      onInput={handleInput}
      className='Editor'
      dangerouslySetInnerHTML={{ __html: content }}
    />
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 03:40:20