React中切换contentEditable为true后无法即时聚焦文本的问题
解决React中contentEditable元素点击斜体文本无法聚焦定位的问题
在React组件中,通过showSpan状态控制可编辑span的显示隐藏。点击div触发mousedown后显示span,普通文本点击能正常聚焦到对应位置,但点击<i>标签内的文本时光标无法定位;同时需要确保仅在聚焦状态下显示span。
关键修复点
- 统一状态变量名:修正原代码中状态定义与更新函数不匹配的问题,保持命名一致性。
- 保存点击位置信息:在mousedown事件中记录点击的DOM节点和偏移量,为后续光标定位提供依据。
- 监听状态变化手动聚焦:使用
useEffect监听showSpan状态切换,在span渲染完成后手动聚焦,并利用Selection API恢复光标到点击位置。 - 确保显示逻辑准确:通过blur事件同步状态,保证span仅在聚焦状态下显示。
完整代码实现
import { useState, useEffect, useRef } from "react"; export default function App() { const [showSpan, setShowSpan] = useState(false); const spanRef = useRef(null); const clickPositionRef = useRef({ node: null, offset: 0 }); const handleMouseDown = (e) => { // 记录点击位置的节点与偏移量 const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); clickPositionRef.current = { node: range.startContainer, offset: range.startOffset }; } setShowSpan(true); // 阻止默认行为,避免浏览器选中原文本 e.preventDefault(); }; useEffect(() => { if (showSpan && spanRef.current) { // 等待DOM更新完成后执行聚焦与光标定位 setTimeout(() => { spanRef.current.focus(); const selection = window.getSelection(); selection.removeAllRanges(); // 在span中匹配原点击的对应节点 const targetNode = findCorrespondingNode(spanRef.current, clickPositionRef.current.node); if (targetNode) { const range = document.createRange(); range.setStart(targetNode, clickPositionRef.current.offset); range.setEnd(targetNode, clickPositionRef.current.offset); selection.addRange(range); } }, 0); } }, [showSpan]); // 递归查找span内与原点击节点对应的目标节点 const findCorrespondingNode = (parent, target) => { if (parent.textContent === target.textContent && parent.tagName === target.tagName) { return parent; } for (let child of parent.children) { const result = findCorrespondingNode(child, target); if (result) return result; } if (parent.nodeType === Node.TEXT_NODE && parent.textContent === target.textContent) { return parent; } return null; }; return ( <> <div onMouseDown={handleMouseDown}> {showSpan ? ( <span ref={spanRef} contentEditable={true} onFocus={() => console.log("focus")} onBlur={() => setShowSpan(false)} > Text can focus first time <br /> <i>Text cannot focus first time</i> </span> ) : ( <> Text can focus first time <br /> <i>Text cannot focus first time</i> </> )} </div> </> ); }
代码说明
- 状态与Ref管理:用
showSpan控制span显示,spanRef获取可编辑元素实例,clickPositionRef缓存点击位置的节点信息。 - mousedown事件处理:捕获当前点击的Range信息并保存,阻止默认行为避免原文本被选中,触发状态更新显示span。
- 光标定位逻辑:在span渲染完成后,通过递归匹配找到对应节点,使用Range和Selection API将光标精准定位到点击位置。
- 节点匹配函数:通过文本内容和标签类型匹配,确保在span的DOM结构中找到与原点击位置对应的节点。
内容的提问来源于stack exchange,提问作者pcldev
相关产品推荐
相关产品推荐

