You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用纯JS实现类似Github的@提及用户下拉列表功能

实现方案参考

可选成熟轻量插件

如果不想从零开发,以下几个无框架依赖的纯JS库可以直接满足需求:

  • tributejs:专门做@提及功能的专用库,支持自定义下拉模板、异步搜索用户、自定义匹配规则,压缩后仅10KB左右,引入即可使用
  • mention.js:超轻量小库,适合简单的@提及场景,配置项少上手门槛极低
  • at.js:早期依赖jQuery的经典@提及库,现在已有纯JS重构版本,功能全面,支持高度自定义下拉样式和交互逻辑

原生JS完整实现方案

不需要插件的话,可以直接用以下代码实现核心功能,可根据自己的需求调整扩展:

HTML结构

<div class="mention-wrapper">
  <textarea id="comment-area" placeholder="输入@即可提及用户"></textarea>
  <ul id="user-dropdown" class="dropdown-hide"></ul>
</div>

CSS样式

.mention-wrapper {
  position: relative;
  width: 500px;
}
#comment-area {
  width: 100%;
  height: 160px;
  padding: 8px;
  box-sizing: border-box;
  border: 1px solid #d1d5da;
  border-radius: 6px;
}
#user-dropdown {
  position: absolute;
  top: 32px;
  left: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
  width: 220px;
  border: 1px solid #e1e4e8;
  border-radius: 6px;
  background: #fff;
  box-shadow: 0 8px 24px rgba(149,157,165,0.2);
  max-height: 300px;
  overflow-y: auto;
  z-index: 999;
}
#user-dropdown li {
  padding: 8px 12px;
  cursor: pointer;
  font-size: 14px;
}
#user-dropdown li:hover {
  background-color: #f6f8fa;
}
.dropdown-hide {
  display: none;
}

JS核心逻辑

// 模拟用户数据,实际使用可替换为接口请求返回的用户列表
const userList = [
  {id: 1, nickname: '张三', username: 'zhangsan'},
  {id: 2, nickname: '李四', username: 'lisi'},
  {id: 3, nickname: '王五', username: 'wangwu'},
  {id: 4, nickname: '赵六', username: 'zhaoliu'}
]

const textarea = document.getElementById('comment-area')
const dropdown = document.getElementById('user-dropdown')
let atIndex = -1 // 记录@符号所在的位置

// 监听textarea输入事件
textarea.addEventListener('input', e => {
  const cursorPos = e.target.selectionStart
  const content = e.target.value
  const preCursorContent = content.slice(0, cursorPos)
  
  // 匹配光标前最近的@符号,且@后无空格
  const matchRes = preCursorContent.match(/@([^\s]*)$/)
  if (matchRes) {
    atIndex = preCursorContent.lastIndexOf('@')
    const searchKey = matchRes[1].toLowerCase()
    // 过滤匹配的用户
    const matchedUsers = userList.filter(user => 
      user.nickname.toLowerCase().includes(searchKey) || user.username.toLowerCase().includes(searchKey)
    )
    renderUserList(matchedUsers)
  } else {
    hideDropdown()
  }
})

// 渲染用户下拉列表
function renderUserList(users) {
  if (users.length === 0) {
    hideDropdown()
    return
  }
  dropdown.innerHTML = users.map(user => 
    `<li data-username="${user.username}">${user.nickname} (@${user.username})</li>`
  ).join('')
  dropdown.classList.remove('dropdown-hide')
}

// 隐藏下拉列表
function hideDropdown() {
  dropdown.classList.add('dropdown-hide')
  atIndex = -1
}

// 点击选中用户插入内容
dropdown.addEventListener('click', e => {
  if (e.target.tagName === 'LI') {
    const selectedUsername = e.target.dataset.username
    const originContent = textarea.value
    // 替换@后的搜索内容为选中的用户名,加空格结束匹配
    const newContent = originContent.slice(0, atIndex) + `@${selectedUsername} ` + originContent.slice(textarea.selectionStart)
    textarea.value = newContent
    hideDropdown()
    // 光标定位到插入内容末尾
    textarea.focus()
    const newCursorPos = atIndex + selectedUsername.length + 2
    textarea.setSelectionRange(newCursorPos, newCursorPos)
  }
})

// 点击页面其他区域关闭下拉
document.addEventListener('click', e => {
  if (!textarea.contains(e.target) && !dropdown.contains(e.target)) {
    hideDropdown()
  }
})

扩展说明

以上代码已经实现了核心的@提及功能,你可以根据需求额外扩展键盘上下键选择用户、异步拉取用户列表、自定义下拉项渲染模板、高亮匹配关键词等功能。

内容的提问来源于stack exchange,提问作者Owaiz Yusufi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 18:15:05