Code Editor组件语法高亮实现后光标跳转至底部问题求助
解决ContentEditable语法高亮时光标跳转问题
你的问题出在每次输入都直接把带高亮标签的HTML更新到state,导致ContentEditable组件重新渲染后光标位置丢失,默认跳到内容末尾。要解决这个问题,核心是在更新高亮内容前后,手动保存并恢复光标位置。
具体实现步骤
- 用
useRef获取ContentEditable的DOM节点,方便操作光标 - 在处理输入前,记录光标在原始纯文本中的位置(高亮后的HTML会插入额外标签,直接用DOM光标位置不对应)
- 生成高亮HTML后,计算光标在新HTML中的对应位置并恢复
修改后的完整代码
import { useState, useRef } from 'react'; import ContentEditable from '@draft-js-plugins/content-editable'; // 按实际使用的包调整 const CodeEditor = () => { const [currCode, setCurrCode] = useState(''); const editorRef = useRef(null); const keywords = ['class', 'int', 'string', 'public', 'static', 'void', 'return']; // 获取光标在纯文本中的位置(忽略HTML标签) const getPlainTextCursorPos = () => { const element = editorRef.current; if (!element) return 0; const selection = window.getSelection(); if (selection.rangeCount === 0) return 0; const range = selection.getRangeAt(0); const preRange = range.cloneRange(); preRange.selectNodeContents(element); preRange.setEnd(range.startContainer, range.startOffset); return preRange.toString().length; }; // 恢复光标到对应位置 const restoreCursor = (targetPos) => { const element = editorRef.current; if (!element) return; const selection = window.getSelection(); const range = document.createRange(); let currentLength = 0; let found = false; const traverseNodes = (node) => { if (found) return; if (node.nodeType === Node.TEXT_NODE) { if (currentLength + node.textContent.length >= targetPos) { range.setStart(node, targetPos - currentLength); range.setEnd(node, targetPos - currentLength); found = true; return; } currentLength += node.textContent.length; } else { for (let i = 0; i < node.childNodes.length; i++) { traverseNodes(node.childNodes[i]); } } }; traverseNodes(element); selection.removeAllRanges(); selection.addRange(range); }; const highlightKeywords = (text) => { return keywords.reduce((acc, keyword) => { const regex = new RegExp(`\\b${keyword}\\b`, 'g'); return acc.replace(regex, (match) => `<span style="color: yellow;">${match}</span>`); }, text); }; const handleCodeChange = (e) => { const plainText = e.target.value; // 1. 保存当前光标位置 const cursorPos = getPlainTextCursorPos(); // 2. 生成高亮内容 const highlightedCode = highlightKeywords(plainText); // 3. 更新状态 setCurrCode(highlightedCode); // 4. 延迟恢复光标(等待组件渲染完成) setTimeout(() => restoreCursor(cursorPos), 0); }; return ( <ContentEditable ref={editorRef} html={currCode} tagName="pre" onChange={handleCodeChange} /> ); }; export default CodeEditor;
关键说明
getPlainTextCursorPos:计算光标在纯文本中的真实位置,不受HTML高亮标签干扰restoreCursor:遍历高亮后的DOM节点,把光标定位到对应纯文本位置setTimeout延迟恢复光标,确保组件完成重新渲染后再操作光标
这样修改后,输入时光标不会再跳到底部,语法高亮功能也能正常生效。
内容的提问来源于stack exchange,提问作者Bunty
相关产品推荐
相关产品推荐

