You need to enable JavaScript to run this app. 如何在contenteditable="true"的元素中输入时为指定单词动态变色
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 12:15:05