You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:47:18