基于JavaScript的简易代码编辑器光标异常问题及解决方案咨询
修复原生JS代码编辑器光标跳转问题(保留代码着色)& CodeMirror替代方案
一、原生代码修复方案
问题根源:每次调用applyColoring()时,通过innerHTML完全替换编辑器DOM结构,导致原有光标位置丢失;且changeColor()里强制将光标定位到最后一个子元素的第1个字符位置,这就是光标跳走的直接原因。
修复思路:在重写DOM前保存光标在文本中的绝对偏移量,重写后再根据偏移量恢复光标位置。
修改后的完整JS代码:
// 保存光标在文本中的偏移量 function getCursorOffset(element) { const selection = window.getSelection(); if (selection.rangeCount === 0) return 0; const range = selection.getRangeAt(0); const preRange = document.createRange(); preRange.selectNodeContents(element); preRange.setEnd(range.startContainer, range.startOffset); return preRange.toString().length; } // 恢复光标到指定偏移量位置 function setCursorOffset(element, offset) { const selection = window.getSelection(); const range = document.createRange(); range.setStart(element, 0); range.collapse(true); const nodeStack = [element]; let currentNode = null; let charCount = 0; let found = false; while (nodeStack.length > 0 && !found) { currentNode = nodeStack.pop(); if (currentNode.nodeType === Node.TEXT_NODE) { const textLength = currentNode.length; if (charCount + textLength >= offset) { range.setStart(currentNode, offset - charCount); range.collapse(true); found = true; } else { charCount += textLength; } } else { // 反向遍历子节点,保证顺序正确 for (let i = currentNode.childNodes.length - 1; i >= 0; i--) { nodeStack.push(currentNode.childNodes[i]); } } } selection.removeAllRanges(); selection.addRange(range); element.focus(); } function applyColoring(element) { var keywords = ["DIV", "SPAN", "H1", "P"]; var keywords2 = ["CLASS", "ID"]; str = element.innerText; const chunks = str .split(new RegExp(keywords.map((w) => `(${w})`).join("|"), "i")) .filter(Boolean); const markup = chunks.reduce((acc, chunk) => { acc += keywords.includes(chunk.toUpperCase()) ? `<span class="statement">${chunk}</span>` : `<span class='other'>${chunk}</span>`; return acc; }, ""); element.innerHTML = markup; } // CHANGE COLOR function changeColor() { var editor = document.getElementById("html"); // 保存光标偏移量 const cursorOffset = getCursorOffset(editor); applyColoring(editor); // 恢复光标位置 setCursorOffset(editor, cursorOffset); } // PREVIEW const elHTML = document.querySelector("#html"); const elPreview = document.querySelector("#preview"); function showPreview() { const html = elHTML.textContent; const dataURL = "data:text/html;charset=utf-8," + encodeURIComponent(html); changeColor(); elPreview.src = dataURL; } changeColor() showPreview()
CSS和HTML部分保持原有代码不变,仅替换上述JS代码即可解决光标跳转问题。
二、CodeMirror替代方案
如果不想手动维护光标和着色逻辑,CodeMirror是更成熟的选择,内置完善的代码高亮、光标管理等功能,基础实现示例如下:
HTML
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/theme/monokai.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/xml/xml.min.js"></script> <div id="editor"></div> <h3>PREVIEW</h3> <div class="preview-area"> <iframe id="preview"></iframe> </div>
JS
// 初始化CodeMirror编辑器 const editor = CodeMirror(document.getElementById('editor'), { value: '<h1>This is a Heading</h1>\n<p>This is a paragraph.</p>', mode: 'xml', // HTML语法模式 theme: 'monokai', // 深色主题 lineNumbers: true, indentUnit: 2, tabSize: 2, lineWrapping: true }); const elPreview = document.querySelector("#preview"); // 实时同步预览 editor.on('change', () => { const html = editor.getValue(); const dataURL = "data:text/html;charset=utf-8," + encodeURIComponent(html); elPreview.src = dataURL; }); // 触发初始预览 editor.trigger('change');
CSS
#editor { width: 456px; height: 267px; } #preview { display: block; width: 100%; height: 300px; }
CodeMirror无需手动处理光标定位和语法着色,直接配置对应语言模式即可获得专业编辑器体验。
内容的提问来源于stack exchange,提问作者Nodigap
相关产品推荐
相关产品推荐

