React中contenteditable div光标跳转问题:如何保持光标在末尾
修复contenteditable div光标跳转及文本反向问题
你的问题根源在于每次通过innerHTML替换整个内容时,浏览器会重置光标位置到元素起始处,导致输入时光标跳开头,视觉上呈现文本反向的效果。同时当前代码的依赖逻辑存在潜在循环触发风险(若text直接取自div的innerText)。
解决方案:更新内容后手动恢复光标位置
修改useEffect逻辑,在设置新的innerHTML后,通过Range和Selection API将光标强制移到div末尾:
import { useEffect, useRef, useState } from 'react'; export default function EditableDiv() { const [text, setText] = useState(''); const divRef = useRef(null); const handleInput = (e) => { setText(e.currentTarget.innerText); }; useEffect(() => { const div = divRef.current; if (!div || !text) return; // 将文本处理为带strong标签的HTML const htmlArray = text.split(' ').map(word => { return `<strong style="color: red">${word} </strong>`; }); const newHtml = htmlArray.join(''); div.innerHTML = newHtml; // 定位光标到div内容末尾 const range = document.createRange(); const selection = window.getSelection(); range.selectNodeContents(div); range.collapse(false); // false表示折叠到内容末尾 selection.removeAllRanges(); selection.addRange(range); }, [text]); return ( <div ref={divRef} contentEditable onInput={handleInput} style={{ border: '1px solid #ddd', padding: '10px', minHeight: '50px' }} /> ); }
关键说明
- 光标定位逻辑:通过
document.createRange()创建选区范围,range.collapse(false)将光标折叠到内容末尾,再通过window.getSelection()设置选区,确保光标始终停在输入内容的最后。 - 状态依赖优化:使用独立的
text状态存储输入内容,避免直接依赖div的innerText导致的无限循环更新。 - 文本处理细节:若不需要每个单词后额外添加空格,可调整
map逻辑为${word},最后再统一处理整体空格问题。
进阶优化(可选)
如果希望光标停在用户输入的原位置(而非强制末尾),需要在更新innerHTML前记录光标相对于文本的偏移量,更新后重新计算并定位光标,这需要更复杂的选区偏移量计算逻辑。
内容的提问来源于stack exchange,提问作者Tarun
相关产品推荐
相关产品推荐

