如何实现支持多类型标签的tags-input输入框?附参考示例
我刚好做过类似的标签输入组件,给你一个完整的实现示例,完全匹配你提到的需求——输入时展示不同类型的候选标签,选中后还能通过样式区分标签类型。下面是可直接运行的代码:
1. HTML 结构
先搭建基础的DOM结构,包含已选标签容器、输入框和候选下拉列表:
<div class="tags-input-container"> <div class="selected-tags" id="selectedTags"></div> <input type="text" class="tags-input" id="tagsInput" placeholder="输入品牌/车型..." > <div class="tag-suggestions" id="tagSuggestions"></div> </div>
2. CSS 样式
给不同类型的标签设置差异化样式,同时美化整个输入区域:
.tags-input-container { width: 400px; border: 1px solid #ddd; border-radius: 6px; padding: 8px; font-family: Arial, sans-serif; } .selected-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; } .tag-item { padding: 4px 8px; border-radius: 4px; color: white; font-size: 14px; display: flex; align-items: center; gap: 4px; } .tag-item .remove-tag { cursor: pointer; font-weight: bold; } /* 不同类型标签的样式 */ .tag-brand { background-color: #2563eb; } .tag-model { background-color: #16a34a; } .tags-input { width: 100%; border: none; outline: none; font-size: 14px; padding: 4px 0; } .tag-suggestions { margin-top: 8px; border-top: 1px solid #eee; padding-top: 8px; max-height: 200px; overflow-y: auto; } .suggestion-item { padding: 6px 8px; cursor: pointer; border-radius: 4px; font-size: 14px; margin-bottom: 4px; } .suggestion-item:hover { background-color: #f3f4f6; } /* 候选标签也区分类型 */ .suggestion-brand { border-left: 3px solid #2563eb; } .suggestion-model { border-left: 3px solid #16a34a; }
3. JavaScript 逻辑
处理输入过滤、标签选中、删除等核心交互:
// 模拟不同类型的标签数据,实际项目可以换成接口返回 const tagsData = [ { id: 1, name: "丰田", type: "brand" }, { id: 2, name: "本田", type: "brand" }, { id: 3, name: "卡罗拉", type: "model" }, { id: 4, name: "思域", type: "model" }, { id: 5, name: "大众", type: "brand" }, { id: 6, name: "朗逸", type: "model" } ]; const selectedTagsContainer = document.getElementById('selectedTags'); const tagsInput = document.getElementById('tagsInput'); const tagSuggestions = document.getElementById('tagSuggestions'); // 存储已选中的标签ID,避免重复选择 const selectedTagIds = new Set(); // 输入事件:过滤并显示候选标签 tagsInput.addEventListener('input', function(e) { const inputValue = e.target.value.toLowerCase(); const filteredTags = tagsData.filter(tag => tag.name.toLowerCase().includes(inputValue) && !selectedTagIds.has(tag.id) ); renderSuggestions(filteredTags); }); // 渲染候选标签列表 function renderSuggestions(tags) { tagSuggestions.innerHTML = ''; if (tags.length === 0) { tagSuggestions.style.display = 'none'; return; } tagSuggestions.style.display = 'block'; tags.forEach(tag => { const suggestionItem = document.createElement('div'); suggestionItem.className = `suggestion-item suggestion-${tag.type}`; suggestionItem.textContent = tag.name; suggestionItem.dataset.tagId = tag.id; suggestionItem.dataset.tagName = tag.name; suggestionItem.dataset.tagType = tag.type; // 点击候选标签,添加到已选列表 suggestionItem.addEventListener('click', function() { addSelectedTag(tag.id, tag.name, tag.type); tagsInput.value = ''; tagSuggestions.innerHTML = ''; tagSuggestions.style.display = 'none'; }); tagSuggestions.appendChild(suggestionItem); }); } // 添加已选标签 function addSelectedTag(id, name, type) { if (selectedTagIds.has(id)) return; const tagItem = document.createElement('div'); tagItem.className = `tag-item tag-${type}`; tagItem.innerHTML = ` ${name} <span class="remove-tag" data-tag-id="${id}">×</span> `; // 删除标签事件 tagItem.querySelector('.remove-tag').addEventListener('click', function() { selectedTagIds.delete(id); tagItem.remove(); }); selectedTagsContainer.appendChild(tagItem); selectedTagIds.add(id); } // 点击页面其他区域,关闭候选列表 document.addEventListener('click', function(e) { if (!e.target.closest('.tags-input-container')) { tagSuggestions.style.display = 'none'; } });
关键说明
- 标签类型区分:通过
tag-brand和tag-model类来给不同类型的标签设置不同样式,你可以根据需求扩展更多类型 - 重复选择限制:用
Set存储已选中的标签ID,避免重复添加 - 输入过滤:实时根据输入内容过滤候选标签,同时排除已选中的标签
- 交互优化:点击候选标签自动添加,点击标签上的叉号删除,点击页面其他区域关闭候选列表
如果需要对接后端接口,只需要把tagsData替换成接口返回的标签数据即可,逻辑完全通用。
内容的提问来源于stack exchange,提问作者Allen Haley
相关产品推荐
相关产品推荐

