contenteditable的code元素结合highlight.js输入文本反转问题求助
解决方案:contenteditable + highlight.js 实时高亮无异常实现
核心问题原因
直接在input事件中调用hljs.highlightElement()会完全重写<code>标签的内部HTML,破坏原生编辑的光标状态、换行逻辑,进而出现光标错位、输入反转、无法换行等问题。
具体解决步骤
1. 缓存并恢复光标位置
执行高亮前先记录当前光标在文本中的偏移量,高亮完成后根据偏移量重新定位光标,避免光标丢失或错位。
2. 适配换行逻辑
给<code>标签添加CSS样式,确保原生换行和手动输入的回车能正常显示;同时在高亮处理时保留换行符对应的格式。
3. 防抖优化
对高亮操作添加防抖,避免输入时频繁触发重渲染,提升编辑流畅度。
完整实现代码
<div class="code-editor"> <pre><code contenteditable="true" class="language-javascript">// 在这里输入代码试试 function test() { console.log("hello"); } </code></pre> </div> <style> .code-editor pre { background: #f5f5f5; padding: 1rem; border-radius: 4px; } .code-editor code[contenteditable="true"] { display: block; white-space: pre-wrap; /* 支持自动换行和手动换行 */ outline: none; /* 去掉默认编辑框线 */ min-height: 100px; } </style> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script> <script> const codeEl = document.querySelector('code[contenteditable="true"]'); let debounceTimer = null; // 保存光标位置 function saveCursorPosition(el) { const selection = window.getSelection(); if (selection.rangeCount === 0) return null; const range = selection.getRangeAt(0); const preRange = range.cloneRange(); preRange.selectNodeContents(el); preRange.setEnd(range.startContainer, range.startOffset); return preRange.toString().length; } // 恢复光标位置 function restoreCursorPosition(el, pos) { const selection = window.getSelection(); const range = document.createRange(); range.setStart(el.firstChild, pos); range.collapse(true); selection.removeAllRanges(); selection.addRange(range); } // 实时高亮处理 function handleHighlight() { const cursorPos = saveCursorPosition(codeEl); const originalText = codeEl.textContent; // 用highlight手动处理文本,避免直接重写元素 const result = hljs.highlight(originalText, { language: codeEl.classList[0].replace('language-', '') }); codeEl.innerHTML = result.value; // 恢复光标位置 if (cursorPos !== null) { restoreCursorPosition(codeEl, cursorPos); } } // 绑定输入事件并添加防抖 codeEl.addEventListener('input', () => { clearTimeout(debounceTimer); debounceTimer = setTimeout(handleHighlight, 150); }); // 初始高亮 handleHighlight(); </script>
关键细节说明
- 用
hljs.highlight()代替hljs.highlightElement():手动处理文本高亮后再替换内容,相比直接调用highlightElement更可控,避免元素被完全重写。 white-space: pre-wrap:确保代码中的换行符和手动输入的回车能正常显示,解决无法换行的问题。- 150ms防抖:平衡实时性和性能,避免每输入一个字符就触发一次高亮。
内容的提问来源于stack exchange,提问作者user19485937
相关产品推荐
相关产品推荐

