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

如何用JavaScript实现选中用户添加至搜索输入框(仿Gmail样式)

实现类似Gmail收件人选择的标签式输入功能

核心思路

这种效果并非直接修改输入框的value值,而是用一个容器模拟输入框外观,内部包含已选用户的标签和真实的输入框。用户点击列表项时,创建标签元素插入容器,真实输入框始终留在最后接收新输入,同时支持删除已选标签。

完整实现代码

HTML结构

<!-- 模拟输入框的容器 -->
<div class="input-wrapper" id="inputWrapper">
  <!-- 存放已选用户的标签 -->
  <div class="selected-tags" id="selectedTags"></div>
  <!-- 真实的输入框,用于输入搜索内容 -->
  <input type="text" class="search-input" id="searchInput" placeholder="输入或选择用户...">
</div>

<!-- 用户列表 -->
<ul class="user-list" id="userList">
  <li data-email="alice@example.com">Alice Smith</li>
  <li data-email="bob@example.com">Bob Johnson</li>
  <li data-email="charlie@example.com">Charlie Brown</li>
</ul>

CSS样式

.input-wrapper {
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 6px 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  min-height: 32px;
  width: 400px;
}

.selected-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.tag {
  background-color: #e0e7ff;
  color: #3730a3;
  padding: 4px 8px;
  border-radius: 4px;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 14px;
}

.tag .remove-btn {
  cursor: pointer;
  font-weight: bold;
  font-size: 16px;
  line-height: 1;
}

.search-input {
  border: none;
  outline: none;
  flex: 1;
  min-width: 120px;
  font-size: 14px;
}

.user-list {
  list-style: none;
  padding: 0;
  margin: 10px 0;
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 400px;
}

.user-list li {
  padding: 8px 12px;
  cursor: pointer;
  border-bottom: 1px solid #eee;
}

.user-list li:last-child {
  border-bottom: none;
}

.user-list li:hover {
  background-color: #f3f4f6;
}

JavaScript逻辑

// 获取DOM元素
const inputWrapper = document.getElementById('inputWrapper');
const selectedTags = document.getElementById('selectedTags');
const searchInput = document.getElementById('searchInput');
const userList = document.getElementById('userList');

// 给用户列表项绑定点击事件
userList.addEventListener('click', (e) => {
  if (e.target.tagName === 'LI') {
    const userName = e.target.textContent;
    const userEmail = e.target.dataset.email;
    addUserTag(userName, userEmail);
    // 清空搜索输入框内容
    searchInput.value = '';
  }
});

// 创建并添加用户标签
function addUserTag(name, email) {
  // 检查是否已添加该用户,避免重复
  const existingTags = Array.from(selectedTags.querySelectorAll('.tag'));
  const isDuplicate = existingTags.some(tag => tag.dataset.email === email);
  if (isDuplicate) return;

  // 创建标签元素
  const tag = document.createElement('div');
  tag.className = 'tag';
  tag.dataset.email = email;
  
  // 标签内容:用户名+邮箱+删除按钮
  tag.innerHTML = `
    <span>${name} (${email})</span>
    <span class="remove-btn">&times;</span>
  `;

  // 绑定标签删除事件
  tag.querySelector('.remove-btn').addEventListener('click', () => {
    tag.remove();
  });

  // 将标签插入到输入框前方
  selectedTags.appendChild(tag);
  // 让输入框重新获得焦点
  searchInput.focus();
}

// 点击容器区域时自动聚焦输入框
inputWrapper.addEventListener('click', () => {
  searchInput.focus();
});

// 阻止点击标签时触发容器的点击事件,避免焦点跳动
selectedTags.addEventListener('click', (e) => {
  e.stopPropagation();
});

关键功能说明

  • 容器模拟输入框:.input-wrapper通过边框、内边距模拟输入框样式,内部用Flex布局自动排列标签和真实输入框。
  • 标签去重:添加标签前先检查该用户是否已存在,避免重复选择。
  • 焦点管理:点击容器空白区域自动聚焦输入框,点击标签时阻止事件冒泡,避免输入框意外失去焦点。
  • 删除功能:每个标签自带删除按钮,点击后直接移除对应标签。

内容的提问来源于stack exchange,提问作者Gopikrishnan K

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 11:40:51