contenteditable div实时语法高亮时光标位置丢失问题求助
解决contenteditable div实时语法高亮的光标偏移问题
直接替换innerHTML会完全重绘DOM结构,导致浏览器丢失原有光标位置,这就是你每次输入后光标跳转到开头的原因。强行将光标移到末尾的方案只适合在内容末尾编辑,无法满足中间内容编辑的需求。
核心解决思路是:高亮前记录光标在纯文本中的偏移量,高亮完成后根据这个偏移量恢复光标位置,具体实现如下:
const codeElement = document.getElementById('code'); // 保存光标在纯文本中的偏移量 function saveCursorPosition(element) { const selection = window.getSelection(); if (selection.rangeCount === 0) return -1; const range = selection.getRangeAt(0); const preCaretRange = range.cloneRange(); preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.endContainer, range.endOffset); return preCaretRange.toString().length; } // 根据偏移量恢复光标位置 function restoreCursorPosition(element, position) { const selection = window.getSelection(); const range = document.createRange(); // 递归遍历DOM,找到对应偏移量的文本节点 function findTextNodeOffset(node, remainingOffset) { if (node.nodeType === Node.TEXT_NODE) { if (node.length >= remainingOffset) { return { node, offset: remainingOffset }; } else { remainingOffset -= node.length; } } else { for (let child of node.childNodes) { const result = findTextNodeOffset(child, remainingOffset); if (result) return result; } } return null; } const result = findTextNodeOffset(element, position); if (result) { range.setStart(result.node, result.offset); range.collapse(true); selection.removeAllRanges(); selection.addRange(range); } } // 实时高亮函数 function highlight() { // 先保存当前光标位置 const cursorPos = saveCursorPosition(codeElement); if (cursorPos === -1) return; const text = codeElement.textContent; // 优化正则规则,减少误匹配 const controlRegex = /\b(function|const|let)\b/g; const stringRegex = /["'].*?["']/g; // 非贪婪匹配,避免跨字符串匹配 const functionCallRegex = /[a-zA-Z_\d]+\([^)]*\)/g; // 匹配有效函数调用格式 const constantRegex = /[a-zA-Z_\d]+\./g; // 匹配带点的常量(如Math.) let highlightedHTML = text.replace(stringRegex, '<span style="color: #0d0; font-weight: normal;">$&</span>') .replace(controlRegex, '<span style="color: pink;">$&</span>') .replace(functionCallRegex, '<span style="color: #0bb; font-weight: bold;">$&</span>') .replace(constantRegex, '<span style="color: yellow;">$&</span>'); codeElement.innerHTML = highlightedHTML; // 恢复光标位置 restoreCursorPosition(codeElement, cursorPos); } codeElement.addEventListener('input', highlight); highlight();
关键说明
- 光标位置记录:
saveCursorPosition通过计算光标到元素起始位置的纯文本长度,得到一个与DOM结构无关的偏移量,确保高亮后能准确对应。 - 光标位置恢复:
restoreCursorPosition递归遍历高亮后的DOM节点,解决了span嵌套导致的直接定位失败问题,能精准找到对应偏移的文本节点并定位光标。 - 正则优化:将字符串匹配改为非贪婪模式,避免错误匹配多个连续字符串;调整其他正则的匹配规则,减少不必要的误高亮。
内容的提问来源于stack exchange,提问作者kalilamodow
相关产品推荐
相关产品推荐

