React中contentEditable Div输入时光标跳至开头问题求解
React contentEditable 光标跳转、输入反向问题解决方法
问题根源
你当前的实现存在两个核心问题:
- 每次输入触发
onInput后,setContent更新state,React会重新渲染div,dangerouslySetInnerHTML会完全替换div内容,导致光标被强制重置到开头。 - 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
相关产品推荐
相关产品推荐

