在JavaScript输入时为单个字符着色并保持光标位置
解决contenteditable输入实时校验时的光标丢失问题
嘿,这个问题我之前做打字练习工具的时候碰到过!核心问题就是每次我们重新替换contenteditable的innerHTML时,浏览器会重置光标位置,所以关键是先存光标,再渲染,最后恢复光标。下面给你完整的解决方案:
核心思路
- 监听
input事件,每次输入触发校验逻辑 - 处理前先保存当前光标在纯文本中的偏移量(避开DOM结构变化的影响)
- 对比输入内容和目标单词
JAVASCRIPT,生成带红色错误标记的HTML结构 - 更新div的innerHTML后,再把光标恢复到正确的位置
完整代码实现
<div id="editor" contenteditable="true" style="padding: 10px; border: 1px solid #ccc; font-size: 20px;"></div> <script> const targetWord = 'JAVASCRIPT'; const editor = document.getElementById('editor'); // 保存光标在纯文本中的偏移量 function saveCursorPosition(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 restoreCursorPosition(element, position) { const selection = window.getSelection(); let currentOffset = 0; const traverseNodes = node => { if (node.nodeType === Node.TEXT_NODE) { if (currentOffset + node.length >= position) { const range = document.createRange(); range.setStart(node, position - currentOffset); range.collapse(true); selection.removeAllRanges(); selection.addRange(range); return true; } currentOffset += node.length; } else { for (const child of node.childNodes) { if (traverseNodes(child)) return true; } } return false; }; // 处理编辑器为空的情况 if (!element.firstChild) { element.appendChild(document.createTextNode('')); } traverseNodes(element); } // 处理输入内容,生成带错误标记的HTML function processInput(inputText) { let result = ''; for (let i = 0; i < inputText.length; i++) { const char = inputText[i]; // 超过目标长度或字符不匹配时标红,忽略大小写(可按需调整) if (i >= targetWord.length || char.toUpperCase() !== targetWord[i]) { result += `<span style="color: red;">${char}</span>`; } else { result += char; } } return result; } // 监听输入事件 editor.addEventListener('input', () => { // 1. 保存光标位置 const cursorPos = saveCursorPosition(editor); // 2. 获取纯文本内容(过滤HTML标签) const inputText = editor.innerText; // 3. 生成带样式的HTML const processedHTML = processInput(inputText); // 4. 更新编辑器内容 editor.innerHTML = processedHTML; // 5. 恢复光标位置 restoreCursorPosition(editor, cursorPos); }); </script>
关键细节说明
- 光标位置的保存与恢复:我们基于纯文本的偏移量来记录光标,而不是DOM节点的位置——因为处理后的DOM会插入span标签,节点结构会变化,纯文本偏移量更稳定可靠。
- 输入处理逻辑:不仅处理了字符不匹配的标红,还兼容了输入长度超过目标单词的情况(超出部分直接标红),同时默认忽略大小写,你可以根据需求修改判断条件。
- 边界兼容:处理了编辑器为空的情况,避免光标恢复失败。
这样用户输入时,错误字符会实时标红,光标也不会跳走,完全不影响正常的输入体验!
内容的提问来源于stack exchange,提问作者Giancarlo
相关产品推荐
相关产品推荐

