如何在contenteditable="true"的元素中输入时为指定单词动态变色
可编辑div自动高亮指定单词实现方案
核心注意点
直接修改contenteditable元素的innerHTML会导致光标自动跳转到内容末尾,是实现这个需求首先要解决的问题,需要额外加光标位置保存、恢复的逻辑。
原生Vanilla JS 实现
1. 基础样式
.text-area { width: 600px; min-height: 200px; padding: 12px; background: #1e1e1e; color: #fff; border: 1px solid #333; outline: none; } .highlight { color: #f43f5e; font-weight: bold; }
2. 完整JS逻辑
const editableDiv = document.querySelector('.text-area') // 要高亮的关键词,可配置多个 const HIGHLIGHT_KEYWORDS = ['typing'] // 生成匹配正则,gi表示全局匹配、忽略大小写,不需要忽略大小写去掉i即可 const keywordReg = new RegExp(HIGHLIGHT_KEYWORDS.join('|'), 'gi') // 工具函数:获取当前光标在元素内的偏移量 function getCaretOffset(element) { const selection = window.getSelection() if (selection.rangeCount === 0) return 0 const range = selection.getRangeAt(0) const preCaretRange = range.cloneRange() preCaretRange.selectNodeContents(element) preCaretRange.setEnd(range.endContainer, range.endOffset) return preCaretRange.toString().length } // 工具函数:恢复光标到指定偏移量位置 function setCaretOffset(element, offset) { const selection = window.getSelection() const range = document.createRange() range.selectNodeContents(element) const textNodes = [] const nodeStack = [element] let currentNode, charCount = 0, found = false // 遍历所有文本节点计数 while (nodeStack.length > 0) { currentNode = nodeStack.pop() if (currentNode.nodeType === Node.TEXT_NODE) { const nextCharCount = charCount + currentNode.length if (offset >= charCount && offset <= nextCharCount) { range.setStart(currentNode, offset - charCount) range.collapse(true) found = true break } charCount = nextCharCount } else { for (let i = currentNode.childNodes.length - 1; i >= 0; i--) { nodeStack.push(currentNode.childNodes[i]) } } } if (found) { selection.removeAllRanges() selection.addRange(range) } } // 输入事件监听 editableDiv.addEventListener('input', () => { // 保存当前光标位置 const caretOffset = getCaretOffset(editableDiv) // 先取纯文本内容,避免原有标签干扰 const rawText = editableDiv.innerText // 替换关键词为高亮标签 const highlightedHtml = rawText.replace(keywordReg, match => `<span class="highlight">${match}</span>`) // 更新内容 editableDiv.innerHTML = highlightedHtml // 恢复光标位置 setCaretOffset(editableDiv, caretOffset) })
框架实现思路(以Vue3为例)
- 用
v-html渲染处理后的高亮内容,通过input事件同步可编辑div的纯文本内容到绑定变量 - 每次内容更新后,调用和原生方案一致的光标保存、恢复逻辑即可,不需要额外调整核心逻辑
- React实现逻辑同理,在
useEffect监听内容变化后触发光标恢复即可。
扩展优化点
- 如果需要匹配多个关键词,直接往
HIGHLIGHT_KEYWORDS数组里加内容即可 - 如果是内容安全要求高的场景,替换前要对特殊字符(
<、>、&等)做转义处理,避免XSS注入风险 - 如果需要支持复杂富文本格式(比如粗体、斜体等),可以遍历所有文本节点做替换,不要直接覆盖整个
innerHTML,避免已有的格式被清除。
内容的提问来源于stack exchange,提问作者user15788060
相关产品推荐
相关产品推荐

