如何实现PicoBlaze模拟器代码实时高亮且光标不偏移?兼容Firefox52
问题分析与解决方案
问题根源
- 光标位置获取错误:你尝试用
edit.selectionStart是误区——selectionStart/selectionEnd仅适用于<input>或<textarea>,对contenteditable的<div>完全不生效,导致无法正确记录光标位置。 - 光标恢复逻辑错误:
range.setStart(assemblyCodeDiv, start)的第二个参数是子节点索引,不是文本偏移量,根本定位不到正确位置;且替换innerHTML后原DOM节点被销毁,旧Range对象直接失效。 - 递归阻塞未重置:
areWeHighlighting变量未在函数末尾重置,第一次高亮后,后续input事件会被直接拦截,无法触发实时高亮。
修复方案
1. 编写光标位置处理辅助函数
针对contenteditable的<div>,需要通过Range API计算光标相对于纯文本的偏移量,再遍历高亮后的DOM结构恢复位置:
// 遍历DOM节点,根据纯文本偏移量定位光标 function setCursorPosition(el, targetOffset) { const range = document.createRange(); const selection = window.getSelection(); let currentOffset = 0; let found = false; function traverse(node) { if (found) return; if (node.nodeType === Node.TEXT_NODE) { if (currentOffset + node.length >= targetOffset) { const actualOffset = Math.min(targetOffset - currentOffset, node.length); range.setStart(node, actualOffset); range.setEnd(node, actualOffset); found = true; } else { currentOffset += node.length; } } else { for (let i = 0; i < node.childNodes.length; i++) { traverse(node.childNodes[i]); } } } traverse(el); selection.removeAllRanges(); selection.addRange(range); }
2. 修改syntaxHighlighter核心函数
更新高亮逻辑,加入光标保存与恢复步骤:
let areWeHighlighting = false; // 确保变量可全局访问 function syntaxHighlighter() { if (areWeHighlighting) return; areWeHighlighting = true; const assemblyCodeDiv = document.getElementById("assemblyCode"); let cursorOffset = 0; // 保存光标位置:计算相对于纯文本的偏移量 const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); const preRange = document.createRange(); preRange.selectNodeContents(assemblyCodeDiv); preRange.setEnd(range.startContainer, range.startOffset); cursorOffset = preRange.toString().length; } // 原文本转义(保留你的原有逻辑) const assemblyCode = assemblyCodeDiv.innerText.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); // 执行你的语法高亮逻辑,生成highlightedText // ...(此处保留你原有的高亮代码,比如匹配指令、寄存器等) // 更新DOM assemblyCodeDiv.innerHTML = highlightedText; // 恢复光标位置 setCursorPosition(assemblyCodeDiv, cursorOffset); // 重置标记,允许下次触发 areWeHighlighting = false; }
3. 绑定实时触发事件
取消footerScript.js中的注释,绑定input事件:
document.getElementById("assemblyCode").oninput = syntaxHighlighter;
Firefox 52兼容性说明
- Firefox 52完全支持
document.createRange()和window.getSelection(),上述代码无需额外兼容处理。 - 避免使用ES6+高级特性(如箭头函数、可选链等),基础
let/const语法在Firefox 52中可正常使用。
内容的提问来源于stack exchange,提问作者FlatAssembler
相关产品推荐
相关产品推荐

