JavaScript实现输入框内@提及(@mention)的实时变色高亮教程


本教程详细介绍了如何在Web前端实现输入框或文本域中特定文本(如`@提及`)的实时高亮显示。通过巧妙结合`contenteditable`属性的`div`元素和底层高亮`div`的叠加技术,我们可以在用户输入时动态解析并着色匹配的文本,同时保持原生的输入体验,解决了标准`input`和`textarea`元素不支持富文本样式的问题。文章将提供完整的HTML、CSS和JavaScript代码示例及详细解释。

核心思想:双层叠加法

在标准的HTML

该方案的核心是创建两个高度重叠的HTML元素:

  1. 顶层输入区 (contenteditable div):这是一个具有contenteditable属性的div元素,用户在此处进行实际的文本输入。为了实现底层高亮效果的可见性,这个div的文本颜色会被设置为透明 (color: transparent;),但光标颜色 (caret-color: black;) 保持可见,以确保用户能够感知输入位置。
  2. 底层高亮区 (普通 div 或 pre):这是一个普通的div或
    元素,它位于顶层输入区的正下方。当用户在顶层输入区输入文本时,JavaScript会实时获取顶层的内容,进行文本解析(例如使用正则表达式匹配@提及),然后将匹配到的部分用带有特定样式的标签包裹,最终将处理后的HTML内容渲染到这个底层高亮区。

通过这种方式,用户感觉像是在一个支持富文本的输入框中打字,但实际上他们是在一个透明的输入层上操作,而视觉上的高亮效果则由底层元素提供。

HTML 结构

首先,我们需要构建一个包含这两个叠加层的基本HTML结构。一个父容器用于定位,内部包含一个用于高亮的div和一个可编辑的div。

  
  
  
  Hi @Roko, how are you today?



Chat tips:
Use @username to mention a user.
Use Enter to send.
Use Shift+Enter to go to a new line.

在上述代码中:

  • .message 是父容器,用于相对定位其子元素。
  • .pre 是底层高亮区,它将显示经过处理的HTML内容。
  • .text 是顶层输入区,它具有contenteditable属性,允许用户直接编辑其内容。spellcheck="false" 用于禁用拼写检查。

CSS 样式:实现叠加与视觉效果

CSS是实现双层叠加效果的关键。我们需要确保两个层完美对齐,并且顶层输入区能够正确地显示光标但隐藏文本。

* {
  margin: 0;
  box-sizing: border-box;
}

body {
  font: 16px/1.3 sans-serif;
}

.message {
  display: block;
  border: 1px solid #888;
  position: relative; /* 父容器相对定位 */
  width: 100%;
  border: 1px solid #ccc;
}

/* 统一两个层的基本样式,确保文本对齐 */
.message .pre,
.message .text {
  border: 0;
  overflow: scroll; /* 允许滚动 */
  overflow-x: hidden; /* 隐藏水平滚动条 */
  font: inherit; /* 继承父级字体 */
  padding: 10px;
  height: 5rem;
  resize: none; /* 禁止用户调整大小 */
  width: 100%;
  white-space: break-spaces; /* 保留空格和换行 */
  word-wrap: break-word; /* 单词在必要时断行 */
}

/* 顶层输入区的特殊样式:文本透明,光标可见 */
.message .text {
  position: relative; /* 相对定位,确保在底层之上 */
  background: transparent;
  outline: none; /* 移除焦点时的外边框 */
  color: transparent; /* 文本颜色透明 */
  caret-color: black; /* 光标颜色可见 */
}

/* 底层高亮区的特殊样式:绝对定位,覆盖整个父容器 */
.message .pre {
  position: absolute; /* 绝对定位,与父容器对齐 */
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  user-select: none; /* 禁止用户选择底层文本 */
}

/* @提及文本的高亮样式 */
.mention {
  color: fuchsia; /* 紫红色 */
}

关键CSS点:

  • .message 容器设置为 position: relative;。
  • .pre 设置为 position: absolute; 并覆盖整个父容器,确保它与 .text 精确重叠。
  • .text 设置为 color: transparent; 和 caret-color: black;。
  • 非常重要: .pre 和 .text 必须具有完全相同的 font, padding, line-height, white-space, word-wrap 等文本相关样式,以确保两层中的文本在视觉上完美对齐。
  • .pre 的 user-select: none; 防止用户意外选择到底层的高亮文本。

JavaScript 逻辑:实现动态高亮与交互

JavaScript负责监听用户输入,处理文本,并将高亮结果渲染到底层,同时同步滚动。

// 获取所有消息容器
document.querySelectorAll(".message").forEach(el => {
  const elText = el.querySelector(".text"); // 顶层输入区
  const elPre = el.querySelector(".pre");   // 底层高亮区

  /**
   * 实时高亮@提及文本
   * @param {HTMLElement} elText - 顶层contenteditable元素
   * @param {HTMLElement} elPre - 底层高亮显示元素
   */
  const colorMention = (elText, elPre) => {
    // 使用正则表达式匹配@提及,并用包裹
    // (?<=^| )@\p{L}+/gu 解释:
    // (?<=^| ) - 正向后行断言,匹配以行首或空格开头的
    // @        - 匹配字面量@
    // \p{L}+   - 匹配一个或多个Unicode字母(支持中文、日文等)
    // g        - 全局匹配
    // u        - 启用Unicode支持
    elPre.innerHTML = elText.innerHTML.replace(/(?<=^| )@\p{L}+/gu, "$&");
  };

  /**
   * 同步两个元素的滚动位置
   * @param {HTMLElement} elText - 顶层contenteditable元素
   * @param {HTMLElement} elPre - 底层高亮显示元素
   */
  const scrollMirror = (elText, elPre) => {
    elPre.scrollTo(elText.scrollLeft, elText.scrollTop);
  };

  /**
   * 处理键盘事件,特别是Enter键
   * @param {KeyboardEvent} ev - 键盘事件对象
   * @param {HTMLElement} elText - 顶层contenteditable元素
   * @param {HTMLElement} elPre - 底层高亮显示元素
   */
  const handleKey = (ev, elText, elPre) => {
    if (ev.key === "Enter" && !ev.shiftKey) {
      // 如果按Enter键且没有按Shift键,则阻止默认换行行为,并提交消息
      ev.preventDefault();

      const message = elText.innerHTML;
      if (!message.trim()) {
        return; // 消息为空,不提交
      }

      // 您的消息提交逻辑在此处:
      console.log("提交的消息:", message);

      // 清空输入区和高亮区
      elText.innerHTML = "";
      elPre.innerHTML = "";
    } else {
      // 其他按键操作后,同步滚动区域
      scrollMirror(elText, elPre);
    }
  };

  // 绑定事件监听器
  elText.addEventListener("scroll", () => scrollMirror(elText, elPre)); // 滚动时同步
  elText.addEventListener("keyup", () => scrollMirror(elText, elPre));   // 抬起按键时同步(确保光标位置更新后滚动)
  elText.addEventListener("input", () => colorMention(elText, elPre));  // 输入时实时高亮
  elText.addEventListener("keydown", (ev) => handleKey(ev, elText, elPre)); // 按下按键时处理特殊逻辑

  // 初始化:页面加载时进行首次高亮和滚动同步
  colorMention(elText, elPre);
  scrollMirror(elText, elPre);
});

JavaScript逻辑详解:

  1. colorMention(elText, elPre) 函数:

    • 这是实现高亮的核心函数。它获取 elText (顶层输入区) 的 innerHTML。
    • 使用 String.prototype.replace() 方法配合正则表达式 /(?
    • $& 在替换字符串中代表匹配到的整个子串。
    • 将匹配到的文本用 $& 包裹,然后更新 elPre (底层高亮区) 的 innerHTML。
  2. scrollMirror(elText, elPre) 函数:

    • 当用户在 contenteditable 区域滚动时,需要同步底层高亮区的滚动位置,以保持文本对齐。
    • 通过将 elPre.scrollTo() 的参数设置为 elText.scrollLeft 和 elText.scrollTop 来实现。
  3. handleKey(ev, elText, elPre) 函数:

    • 这个函数主要处理 Enter 键的行为。在聊天应用中,通常 Enter 用于发送消息,而 Shift + Enter 用于换行。
    • 如果只按 Enter 键,ev.preventDefault() 会阻止默认的换行行为。
    • 然后,你可以在此处添加提交消息的逻辑。
    • 最后清空输入区。
    • 对于其他按键,仍然调用 scrollMirror 确保滚动同步。
  4. 事件监听器:

    • scroll 事件:当用户滚动 elText 时,同步 elPre 的滚动。
    • keyup 事件:在用户抬起按键后,再次同步滚动,这有助于在某些情况下更精确地同步光标位置和滚动。
    • input 事件:这是最关键的事件,每次用户输入或删除字符时都会触发,用于实时调用 colorMention 进行高亮更新。
    • keydown 事件:用于捕获 Enter 等特殊按键,以便在默认行为发生前进行处理。
  5. 初始化:

    • 在页面加载时,调用 colorMention 和 scrollMirror 一次,确保输入框的初始内容也能被正确高亮和定位。

正则表达式解析

用于匹配 @提及 的正则表达式是 /(?

  • ( ?正向后行断言 (Positive Lookbehind)。它表示匹配必须紧跟在以下两种情况之后:
    • ^:字符串的开头。
    • `:一个空格字符。 这确保了@符号不会匹配到单词中间,例如email@example.com中的@`。
  • @:匹配字面量字符 @。
  • \p{L}+:
    • \p{L}:这是一个Unicode属性转义序列,表示匹配任何Unicode字母字符。这使得 @提及 功能能够支持包含非拉丁字母的用户名(如中文、日文、西里尔字母等)。
    • +:匹配前一个字符或组一次或多次。
  • g:全局匹配 (Global flag)。表示查找所有匹配项,而不是在找到第一个匹配项后停止。
  • u:Unicode模式 (Unicode flag)。启用对Unicode字符的正确处理,特别是对于 \p{L} 这样的Unicode属性转义序列是必需的。

注意事项与总结

  • CSS 精确对齐至关重要: 确保顶层输入区和底层高亮区的 font-size, font-family, line-height, padding, border-width, white-space, word-wrap 等所有可能影响文本布局的CSS属性完全一致。任何微小的差异都可能导致文本错位,破坏用户体验。
  • 性能考量: 对于非常长的文本内容,频繁地进行 innerHTML 操作和正则表达式替换可能会有轻微的性能开销。但在大多数聊天或短文本输入场景下,这种开销通常可以忽略不计。
  • contenteditable 的行为: contenteditable 元素虽然提供了富文本编辑的能力,但其默认行为有时可能与
  • 浏览器兼容性: contenteditable 属性和Unicode正则表达式 (\p{L} 和 u 标志) 在现代浏览器中都有良好的支持。但在非常旧的浏览器中可能会遇到兼容性问题。
  • 功能扩展: 基于此双层叠加模式,你可以进一步扩展功能,例如实现表情符号解析、Markdown预览、代码语法高亮等更复杂的实时富文本效果。

通过上述方法,我们成功地在Web前端实现了一个功能强大且用户体验良好的实时文本高亮输入框,有效解决了标准HTML表单元素在富文本处理方面的局限性。


# css  # javascript  # word  # java  # html  # 前端  # markdown  # go  # 正则表达式  # 浏览器  # ai 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 网络优化76771 】 【 技术知识130152 】 【 IDC云计算60162 】 【 营销推广131313 】 【 AI优化88182 】 【 百度推广37138 】 【 网站推荐60173 】 【 精选阅读31334


相关推荐: Win11输入法选字框不见了怎么办_Win11输入法修复与重置【教程】  Python文件管理规范_工程实践说明【指导】  php订单日志怎么导出excel_php导出订单日志到表格教程【教程】  Win10怎么卸载爱奇艺_Win10彻底卸载爱奇艺方法【步骤】  C#如何使用Channel C#通道实现异步通信  如何在 Go 中比较自定义的数组类型(如 [20]byte)  c++的STL算法库find怎么用 在容器中查找指定元素【实用教程】  Win10如何设置双wan路由器 Win10双wan路由器设置方法【指南】  Mac自带的词典App怎么用_Mac添加和使用多语言词典【技巧】  怎么将XML数据可视化 D3.js加载XML  Windows怎样关闭锁屏广告_Windows关闭锁屏广告方法【教程】  VSC怎么快速定位PHP错误行_错误追踪设置法【方法】  php订单日志怎么按金额排序_php按订单金额排序日志方法【方法】  Win11截图快捷键是什么_Win11自带截图工具使用技巧【汇总】  PHP cURL GET请求:正确设置认证与自定义请求头的完整教程  如何使用Golang写入二进制文件_Golang io Write二进制写入示例  Win10怎样设置多显示器_Win10多显示器扩展设置【攻略】  PHP主流架构怎么集成Redis缓存_配置步骤【方法】  Windows11怎么用“记事本”自动换行与编码 Windows11记事本启用自动换行选择UTF-8编码避免乱码兼容多语言【教程】  短链接怎么自定义还原php_修改解码规则适配需求【汇总】  Win10怎样安装PPT模板_Win10安装PPT模板教程【步骤】  如何使用正则表达式提取以编号开头、后跟多个注解的完整代码块  如何在 ACF 中正确更新嵌套多层 Group 字段内的子字段  php文件怎么变mp4保存_php输出视频流保存为mp4操作【操作】  微信里的php文件怎么变mp4_微信接收php转mp4操作步骤【操作】  Win10怎样卸载自带Edge_Win10卸载Edge浏览器步骤【教程】  Win11任务栏天气怎么关闭 Win11隐藏天气小组件图标【设置】  Win11怎么退出微软账户_切换Win11为本地账户登录方法【详解】  如何在Golang中编写异步函数测试_Golang异步操作测试策略  Windows 11怎么设置默认解压软件_Windows 11为ZIP/RAR文件指定默认打开程序  为什么本地php环境运行php脚本卡顿_php执行效率优化方法与设置【说明】  php怎么捕获异常_trycatch结构处理运行时错误的技巧【方法】  c++如何连接Redis c++ hiredis库使用教程【指南】  Win11右键反应慢怎么办 Win11优化右键菜单加载速度【技巧】  Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】  Golang如何避免指针逃逸_Golang逃逸分析与堆栈优化策略  C++如何使用std::optional?(处理可选值)  Win11怎么设置默认视频播放器_Windows 11关联媒体文件打开方式【步骤】  Win11时间不对怎么同步_Win11自动校准互联网时间【设置】  Bpmn 2.0的XML文件怎么画流程图  Win11怎么制作U盘启动盘_Win11原版系统安装盘制作【详解】  c++怎么处理多线程死锁_c++ lock_guard与unique_lock锁管理【技巧】  如何使用Golang匿名函数_快速定义临时函数逻辑  如何使用正则表达式精确匹配最多含一个换行符的 start-end 区段  c++如何利用doxygen生成开发文档_c++ 代码注释规范与HTML文档导出【案例】  短链接还原php提示内存不足_调整PHP内存限制设置【技巧】  c++ std::atomic如何保证原子性 c++ CAS操作原理【底层】  Python列表推导式与字典推导式教程_简化代码高效写法  c# Task.Yield 的作用是什么 它和Task.Delay(1)有区别吗  Win10怎么卸载金山毒霸_Win10彻底卸载金山毒霸方法【步骤】 

 2025-12-09

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

致胜网络推广营销网


致胜网络推广营销网

致胜网络推广营销网专注海外推广十年,是谷歌推广.Facebook广告全球合作伙伴,我们精英化的技术团队为企业提供谷歌海外推广+外贸网站建设+网站维护运营+Google SEO优化+社交营销为您提供一站式海外营销服务。

 915688610

 17370845950

 915688610@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.