使用contentEditable与onInput时,输入文本后光标跳至开头如何解决?
解决contentEditable输入后光标跳转到开头的问题
问题根源是:当你在onInput中更新状态(比如widget.alterations.title),组件会重新渲染,contentEditable的div被重新生成,光标位置因此重置到开头。要解决这个问题,需要先保存光标位置,再更新状态,最后在组件重新渲染后恢复光标位置。
具体实现步骤
- 用
useRef获取contentEditable元素的引用 - 在
onInput事件触发时,先记录当前的光标/选中范围 - 更新状态后,通过
useEffect监听状态变化,恢复之前保存的光标位置
修改后的代码示例
import { useState, useEffect, useRef } from 'react'; const YourComponent = ({ widget }) => { // 本地维护修改后的状态,避免直接修改props const [alterations, setAlterations] = useState(widget.alterations); // 保存光标选中范围 const [selectionRange, setSelectionRange] = useState(null); // 获取contentEditable元素的引用 const editableRef = useRef(null); const alter = (e) => { // 记录当前光标/选中范围 const selection = window.getSelection(); if (selection.rangeCount > 0) { // 克隆Range对象,避免原Range在DOM更新后失效 setSelectionRange(selection.getRangeAt(0).cloneRange()); } // 获取编辑后的文本内容 const newTitle = e.currentTarget.textContent; // 更新状态 setAlterations(prev => ({ ...prev, title: newTitle })); }; // 状态更新后恢复光标位置 useEffect(() => { if (selectionRange && editableRef.current) { const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(selectionRange); // 重置选中范围,避免重复触发恢复逻辑 setSelectionRange(null); } }, [alterations.title, selectionRange]); return ( <div ref={editableRef} contentEditable data-which="title" onClick={e => { e.stopPropagation(); e.preventDefault(); }} onInput={alter} className={styles.widgetSignalTitleText} > {alterations.title} </div> ); };
关键说明
- 必须克隆
Range对象:原Range对象会在DOM重新渲染后与旧DOM节点绑定,克隆后才能关联到新渲染的节点 - 本地维护状态:避免直接修改传入的
widget.alterationsprops,确保状态更新触发组件重新渲染的逻辑正确 - 恢复光标后重置
selectionRange:防止useEffect重复执行恢复逻辑,导致光标异常
内容的提问来源于stack exchange,提问作者codingrohtak
相关产品推荐
相关产品推荐

