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

实现类似Upwork技能栏的多语言标签选择功能求助

嘿,你已经搭好了基础框架,接下来我们把它改造成Upwork那种丝滑的标签式多选组件!我帮你梳理了完整的实现方案,包括输入联想、标签管理和交互优化,直接上手就能用:

核心功能实现思路

我们需要在现有下拉复选框的基础上,补充三个关键能力:

  • 实时输入联想:监听用户输入,动态过滤匹配的语言选项
  • 标签化管理:将选中的语言转为可删除的标签,支持多选不重复
  • 流畅交互:支持点击选项/回车添加标签、删除标签同步更新选项状态
完整代码实现

HTML 结构(替换你现有的按钮组)

<div class="language-selector">
  <!-- 已选标签容器 -->
  <div class="selected-tags"></div>
  <!-- 用户输入框 -->
  <input type="text" class="language-input" placeholder="输入语言名称..." />
  <!-- 联想下拉列表 -->
  <ul class="suggestion-list">
    <li><label><input type="checkbox" value="JavaScript" /> JavaScript</label></li>
    <li><label><input type="checkbox" value="Python" /> Python</label></li>
    <li><label><input type="checkbox" value="Java" /> Java</label></li>
    <li><label><input type="checkbox" value="C++" /> C++</label></li>
    <li><label><input type="checkbox" value="TypeScript" /> TypeScript</label></li>
    <li><label><input type="checkbox" value="Go" /> Go</label></li>
  </ul>
</div>

CSS 样式(贴近Upwork视觉风格)

.language-selector {
  position: relative;
  width: 450px;
  border: 1px solid #e1e5e9;
  border-radius: 6px;
  padding: 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  background: white;
}

.selected-tags .tag {
  background: #e6f0ff;
  color: #2563eb;
  padding: 4px 10px;
  border-radius: 20px;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: 6px;
}

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

.language-input {
  border: none;
  outline: none;
  flex: 1;
  min-width: 150px;
  padding: 4px 0;
  font-size: 14px;
}

.suggestion-list {
  position: absolute;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  border: 1px solid #e1e5e9;
  border-radius: 0 0 6px 6px;
  background: white;
  list-style: none;
  padding: 0;
  margin: 0;
  max-height: 220px;
  overflow-y: auto;
  box-shadow: 0 4px 12px rgba(0,0,0,0.05);
  display: none;
}

.suggestion-list.show {
  display: block;
}

.suggestion-list li {
  padding: 10px 12px;
  cursor: pointer;
  font-size: 14px;
}

.suggestion-list li:hover {
  background: #f8fafc;
}

.suggestion-list input[type="checkbox"] {
  margin-right: 8px;
  cursor: pointer;
}

JavaScript 交互逻辑

const languageInput = document.querySelector('.language-input');
const suggestionList = document.querySelector('.suggestion-list');
const selectedTagsContainer = document.querySelector('.selected-tags');
const allSuggestionItems = Array.from(suggestionList.querySelectorAll('li'));
const selectedLanguages = new Set(); // 用Set避免重复选中

// 1. 实时输入过滤联想列表
languageInput.addEventListener('input', (e) => {
  const query = e.target.value.toLowerCase().trim();
  suggestionList.classList.add('show');

  allSuggestionItems.forEach(item => {
    const languageName = item.textContent.trim().toLowerCase();
    const checkbox = item.querySelector('input');
    
    // 显示匹配的选项,同步已选中状态
    if (languageName.includes(query)) {
      item.style.display = 'block';
      checkbox.checked = selectedLanguages.has(checkbox.value);
    } else {
      item.style.display = 'none';
    }
  });
});

// 2. 点击联想选项添加/移除标签
suggestionList.addEventListener('click', (e) => {
  const targetLi = e.target.closest('li');
  if (!targetLi) return;

  const checkbox = targetLi.querySelector('input');
  const language = checkbox.value;

  if (selectedLanguages.has(language)) {
    removeTag(language);
    checkbox.checked = false;
  } else {
    addTag(language);
    checkbox.checked = true;
  }

  languageInput.value = ''; // 清空输入框
});

// 3. 回车快速添加第一个匹配的标签
languageInput.addEventListener('keydown', (e) => {
  if (e.key === 'Enter') {
    e.preventDefault();
    // 找到第一个显示且未选中的选项
    const targetItem = allSuggestionItems.find(item => {
      const checkbox = item.querySelector('input');
      return item.style.display !== 'none' && !selectedLanguages.has(checkbox.value);
    });

    if (targetItem) {
      const checkbox = targetItem.querySelector('input');
      addTag(checkbox.value);
      checkbox.checked = true;
      languageInput.value = '';
    }
  }
});

// 4. 点击页面其他区域隐藏联想列表
document.addEventListener('click', (e) => {
  if (!e.target.closest('.language-selector')) {
    suggestionList.classList.remove('show');
  }
});

// 辅助函数:添加标签
function addTag(language) {
  if (selectedLanguages.has(language)) return;

  selectedLanguages.add(language);
  
  const tagElement = document.createElement('div');
  tagElement.className = 'tag';
  tagElement.dataset.language = language;
  tagElement.innerHTML = `
    ${language}
    <span class="remove-btn">&times;</span>
  `;

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

  selectedTagsContainer.appendChild(tagElement);
}

// 辅助函数:移除标签
function removeTag(language) {
  selectedLanguages.delete(language);
  
  const tagElement = selectedTagsContainer.querySelector(`[data-language="${language}"]`);
  if (tagElement) tagElement.remove();

  // 同步更新复选框状态
  const checkbox = suggestionList.querySelector(`input[value="${language}"]`);
  if (checkbox) checkbox.checked = false;
}
关键细节说明
  • 用Set存储已选语言,天然避免重复选中的问题
  • 输入联想时实时过滤选项,同时同步已选中的状态
  • 支持两种添加标签的方式:点击选项或回车快速选择第一个匹配项
  • 删除标签时自动同步下拉列表的复选框状态,保证交互一致性

内容的提问来源于stack exchange,提问作者A.A. F

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:53:46