下拉组件完全指南
AI文章总结
本文系统介绍了7种下拉组件的设计意图、适用场景及选择方法,包括基础、多选、搜索、分组、级联、标签和自定义下拉。核心原则是根据选项数量、选择模式、数据层级和用户预期进行决策,没有“最好”的组件,只有“最合适”的组件。文章提供了决策对照表和使用位置建议,帮助设计师在表单、筛选、导航和数据录入等场景中正确选用下拉组件,以提升操作效率与用户体验。
从基础到进阶 · 7 种下拉组件的设计、选择与交互实践
下拉组件是界面中最常见却又最容易被低估的交互元素。一次好的下拉体验,能让用户感到「刚刚好」。
在本文中,我们将深入探讨 7 种下拉组件 的设计意图、适用场景以及如何根据需求做出正确的选择。无论你是在搭建后台管理系统、电商平台,还是设计复杂的 B2B 工具,这份指南都能帮你找到最合适的下拉方案。
7 种下拉组件速览
- 基础下拉 —— 最经典的单选菜单,适合简单选项。
- 多选下拉 —— 允许用户同时选择多个选项。
- 搜索下拉 —— 带过滤搜索功能,适合选项较多的场景。
- 分组下拉 —— 将选项按类别分组,提升浏览效率。
- 级联下拉 —— 联动选择,后一级选项依赖前一级。
- 标签下拉 —— 以标签形式展示已选项,适合快速添加/移除。
- 自定义下拉 —— 完全定制化,满足品牌或特殊交互需求。
核心原则: 下拉组件的选择应当基于 选项数量、选择模式(单选/多选)、数据层级 以及 用户预期。没有「最好」的组件,只有「最合适」的组件。
如何选择使用?
面对不同的业务场景,我们可以通过以下维度来快速决策:
- 选项数量: 少于 5 个 → 基础下拉 / 标签下拉;多于 10 个 → 搜索下拉或分组下拉。
- 选择模式: 单选 → 基础下拉 / 搜索下拉;多选 → 多选下拉 / 标签下拉。
- 数据依赖: 有级联关系 → 级联下拉;需要分类 → 分组下拉。
- 品牌调性: 需要突出视觉风格 → 自定义下拉。
决策对照表
| 组件类型 | 推荐场景 | 选项数量 | 选择模式 |
|---|---|---|---|
| 基础下拉 | 简单配置、性别、状态 | ≤ 8 | 单选 |
| 多选下拉 | 标签筛选、权限分配 | ≤ 15 | 多选 |
| 搜索下拉 | 城市选择、用户搜索 | ≥ 10 | 单选 / 多选 |
| 分组下拉 | 部门选择、分类目录 | ≥ 10 | 单选 |
| 级联下拉 | 省市区、分类联动 | 动态 | 单选 |
| 标签下拉 | 技能选择、兴趣标签 | ≤ 12 | 多选 |
| 自定义下拉 | 品牌定制、特殊交互 | 任意 | 任意 |
使用位置建议
下拉组件在页面中的位置直接影响用户的操作效率与认知负担。以下是一些通用建议:
- 表单区域: 基础下拉、多选下拉、搜索下拉 是最常见的表单控件,通常与标签、输入框并列。
- 筛选 / 搜索栏: 多选下拉、标签下拉 非常适合作为筛选条件,例如「按标签筛选」「按状态筛选」。
- 导航 / 菜单: 自定义下拉 可以用于用户菜单、设置入口等,与品牌风格保持一致。
- 数据录入: 级联下拉 在地址录入、分类选择等场景中表现优异。
位置技巧: 将下拉组件放在与上下文相关的区域,并确保下拉面板不会遮挡重要的操作按钮。对于长列表,建议将下拉面板向上展开(如果空间允许)。
下面,我们通过一个完整的交互预览,来感受这 7 种下拉组件的实际表现。每个组件都附有简短的说明,你可以直接点击体验。
①基础下拉单选
Basic Dropdown
作用
最经典的单选菜单,适合选项数量较少的简单选择场景。
适合场景
性别、状态、简单配置项等固定短列表。
AGENT 提示词
生成一个单选基础下拉,选项包含「产品设计、前端开发、后端开发、数据分析」。
<div class="dd dd-basic" id="basic">
<button type="button" class="dd-toggle">
<span class="dd-label dd-placeholder">请选择</span>
<i class="fas fa-chevron-down dd-arrow"></i>
</button>
<ul class="dd-menu">
<li data-value="design">产品设计</li>
<li data-value="frontend">前端开发</li>
<li data-value="backend">后端开发</li>
<li data-value="data">数据分析</li>
</ul>
</div>.dd{position:relative;width:100%;min-width:190px;max-width:340px}
.dd .dd-toggle{
width:100%;padding:10px 16px;background:var(--ti-field);border:1.5px solid var(--ti-border);
border-radius:12px;font-size:.95rem;color:var(--ti-fg);cursor:pointer;display:flex;
justify-content:space-between;align-items:center;gap:8px;text-align:left;
transition:border-color .2s,background .2s;
}
.dd .dd-toggle:hover{border-color:var(--ti-accent);background:var(--ti-field-hover)}
.dd .dd-toggle .dd-arrow{transition:transform .2s;flex:none;font-size:.8rem;color:var(--ti-muted)}
.dd.is-open .dd-toggle .dd-arrow{transform:rotate(180deg)}
.dd .dd-menu{
position:absolute;top:calc(100% + 6px);left:0;width:100%;background:var(--ti-panel);
border-radius:14px;box-shadow:var(--ti-shadow);border:1px solid var(--ti-line);
opacity:0;visibility:hidden;transform:translateY(-6px);
transition:opacity .2s,transform .2s,visibility .2s;z-index:100;
list-style:none;margin:0;padding:6px 0;overflow:hidden;
}
.dd.is-open .dd-menu{opacity:1;visibility:visible;transform:translateY(0)}
.dd .dd-menu li{
padding:10px 18px;cursor:pointer;transition:background .15s;font-size:.95rem;
color:var(--ti-fg);list-style:none;display:flex;align-items:center;gap:10px;
}
.dd .dd-menu li:hover{background:var(--ti-accent-ghost)}
.dd .dd-menu li.is-selected{background:var(--ti-accent-soft);font-weight:500;color:var(--ti-accent-deep)}
.dd .dd-placeholder{color:var(--ti-muted)}
.dd .dd-check{width:18px;text-align:center;color:var(--ti-accent);flex:none}
.dd .dd-check.is-hidden{opacity:0}function ddInit(root){
var toggle = root.querySelector('.dd-toggle');
var menu = root.querySelector('.dd-menu');
if (!toggle || !menu) { return null; }
function close(){ root.classList.remove('is-open'); }
function open(){ root.classList.add('is-open'); }
function isOpen(){ return root.classList.contains('is-open'); }
toggle.addEventListener('click', function(e){
e.stopPropagation();
if (isOpen()) { close(); } else { open(); }
});
document.addEventListener('click', function(e){
if (!root.contains(e.target)) { close(); }
});
document.addEventListener('keydown', function(e){
if (e.key === 'Escape') { close(); }
});
return { root: root, toggle: toggle, menu: menu, open: open, close: close, isOpen: isOpen };
}
var dd = ddInit(document.getElementById('basic'));
if (dd) {
var label = dd.root.querySelector('.dd-label');
dd.menu.querySelectorAll('li').forEach(function(li){
li.addEventListener('click', function(e){
e.stopPropagation();
label.textContent = li.textContent.trim();
label.classList.remove('dd-placeholder');
dd.menu.querySelectorAll('li').forEach(function(x){ x.classList.remove('is-selected'); });
li.classList.add('is-selected');
dd.close();
});
});
}②多选下拉多选
Multi-select Dropdown
作用
允许同时选择多个选项,已选项以标签形式展示,便于快速管理。
适合场景
标签筛选、权限分配、兴趣多选等场景。
AGENT 提示词
生成一个多选下拉,已选项以标签展示并可删除,选项含 Vue.js、React、Angular、Svelte。
<div class="dd dd-multi" id="multi">
<div class="dd-toggle" role="button" tabindex="0">
<span class="dd-tags"><span class="dd-placeholder">请选择</span></span>
<i class="fas fa-chevron-down dd-arrow"></i>
</div>
<ul class="dd-menu">
<li data-value="vue"><span class="dd-check is-hidden">✓</span> Vue.js</li>
<li data-value="react"><span class="dd-check is-hidden">✓</span> React</li>
<li data-value="angular"><span class="dd-check is-hidden">✓</span> Angular</li>
<li data-value="svelte"><span class="dd-check is-hidden">✓</span> Svelte</li>
</ul>
</div>.dd{position:relative;width:100%;min-width:190px;max-width:340px}
.dd .dd-toggle{
width:100%;padding:10px 16px;background:var(--ti-field);border:1.5px solid var(--ti-border);
border-radius:12px;font-size:.95rem;color:var(--ti-fg);cursor:pointer;display:flex;
justify-content:space-between;align-items:center;gap:8px;text-align:left;
transition:border-color .2s,background .2s;
}
.dd .dd-toggle:hover{border-color:var(--ti-accent);background:var(--ti-field-hover)}
.dd .dd-toggle .dd-arrow{transition:transform .2s;flex:none;font-size:.8rem;color:var(--ti-muted)}
.dd.is-open .dd-toggle .dd-arrow{transform:rotate(180deg)}
.dd .dd-menu{
position:absolute;top:calc(100% + 6px);left:0;width:100%;background:var(--ti-panel);
border-radius:14px;box-shadow:var(--ti-shadow);border:1px solid var(--ti-line);
opacity:0;visibility:hidden;transform:translateY(-6px);
transition:opacity .2s,transform .2s,visibility .2s;z-index:100;
list-style:none;margin:0;padding:6px 0;overflow:hidden;
}
.dd.is-open .dd-menu{opacity:1;visibility:visible;transform:translateY(0)}
.dd .dd-menu li{
padding:10px 18px;cursor:pointer;transition:background .15s;font-size:.95rem;
color:var(--ti-fg);list-style:none;display:flex;align-items:center;gap:10px;
}
.dd .dd-menu li:hover{background:var(--ti-accent-ghost)}
.dd .dd-menu li.is-selected{background:var(--ti-accent-soft);font-weight:500;color:var(--ti-accent-deep)}
.dd .dd-placeholder{color:var(--ti-muted)}
.dd .dd-check{width:18px;text-align:center;color:var(--ti-accent);flex:none}
.dd .dd-check.is-hidden{opacity:0}
.dd-multi .dd-toggle{min-height:46px;flex-wrap:wrap}
.dd-multi .dd-tags{display:flex;flex-wrap:wrap;gap:4px;flex:1}
.dd-multi .dd-tag{
background:var(--ti-accent-soft);padding:2px 8px 2px 12px;border-radius:30px;
font-size:.8rem;color:var(--ti-accent-deep);display:inline-flex;align-items:center;
gap:6px;font-weight:500;
}
.dd-multi .dd-tag i{cursor:pointer;font-size:.7rem;opacity:.7;transition:opacity .2s}
.dd-multi .dd-tag i:hover{opacity:1}
.dd-multi .dd-menu{max-height:200px;overflow-y:auto}function ddInit(root){
var toggle = root.querySelector('.dd-toggle');
var menu = root.querySelector('.dd-menu');
if (!toggle || !menu) { return null; }
function close(){ root.classList.remove('is-open'); }
function open(){ root.classList.add('is-open'); }
function isOpen(){ return root.classList.contains('is-open'); }
toggle.addEventListener('click', function(e){
e.stopPropagation();
if (isOpen()) { close(); } else { open(); }
});
document.addEventListener('click', function(e){
if (!root.contains(e.target)) { close(); }
});
document.addEventListener('keydown', function(e){
if (e.key === 'Escape') { close(); }
});
return { root: root, toggle: toggle, menu: menu, open: open, close: close, isOpen: isOpen };
}
var dd = ddInit(document.getElementById('multi'));
if (dd) {
var box = dd.root.querySelector('.dd-tags');
var picked = [];
function labelOf(val){
var li = dd.menu.querySelector('li[data-value="' + val + '"]');
return li ? li.textContent.trim() : val;
}
function render(){
if (!picked.length) {
box.innerHTML = '<span class="dd-placeholder">请选择</span>';
return;
}
box.innerHTML = picked.map(function(v){
return '<span class="dd-tag">' + labelOf(v) +
' <i class="fas fa-times" data-value="' + v + '"></i></span>';
}).join('');
box.querySelectorAll('.dd-tag i').forEach(function(icon){
icon.addEventListener('click', function(e){
e.stopPropagation();
toggleVal(icon.getAttribute('data-value'));
});
});
}
function toggleVal(val){
var i = picked.indexOf(val);
var li = dd.menu.querySelector('li[data-value="' + val + '"]');
var check = li ? li.querySelector('.dd-check') : null;
if (i > -1) {
picked.splice(i, 1);
if (check) { check.classList.add('is-hidden'); }
} else {
picked.push(val);
if (check) { check.classList.remove('is-hidden'); }
}
render();
}
dd.menu.querySelectorAll('li').forEach(function(li){
li.addEventListener('click', function(e){
e.stopPropagation();
toggleVal(li.getAttribute('data-value'));
});
});
}③搜索下拉带过滤
Searchable Dropdown
作用
内置关键词过滤,适合选项较多的数据检索场景。
适合场景
城市选择、用户搜索、长列表检索等。
AGENT 提示词
生成一个带搜索过滤的下拉,输入关键词实时筛选城市列表。
<div class="dd dd-search" id="search">
<button type="button" class="dd-toggle">
<span class="dd-label dd-placeholder">选择城市</span>
<i class="fas fa-chevron-down dd-arrow"></i>
</button>
<div class="dd-menu">
<input type="text" class="dd-input" placeholder="搜索城市…" />
<ul class="dd-options">
<li data-value="beijing">北京</li>
<li data-value="shanghai">上海</li>
<li data-value="guangzhou">广州</li>
<li data-value="shenzhen">深圳</li>
<li data-value="hangzhou">杭州</li>
<li data-value="chengdu">成都</li>
<li data-value="wuhan">武汉</li>
</ul>
<div class="dd-empty" hidden>未找到匹配项</div>
</div>
</div>.dd{position:relative;width:100%;min-width:190px;max-width:340px}
.dd .dd-toggle{
width:100%;padding:10px 16px;background:var(--ti-field);border:1.5px solid var(--ti-border);
border-radius:12px;font-size:.95rem;color:var(--ti-fg);cursor:pointer;display:flex;
justify-content:space-between;align-items:center;gap:8px;text-align:left;
transition:border-color .2s,background .2s;
}
.dd .dd-toggle:hover{border-color:var(--ti-accent);background:var(--ti-field-hover)}
.dd .dd-toggle .dd-arrow{transition:transform .2s;flex:none;font-size:.8rem;color:var(--ti-muted)}
.dd.is-open .dd-toggle .dd-arrow{transform:rotate(180deg)}
.dd .dd-menu{
position:absolute;top:calc(100% + 6px);left:0;width:100%;background:var(--ti-panel);
border-radius:14px;box-shadow:var(--ti-shadow);border:1px solid var(--ti-line);
opacity:0;visibility:hidden;transform:translateY(-6px);
transition:opacity .2s,transform .2s,visibility .2s;z-index:100;
list-style:none;margin:0;padding:6px 0;overflow:hidden;
}
.dd.is-open .dd-menu{opacity:1;visibility:visible;transform:translateY(0)}
.dd .dd-menu li{
padding:10px 18px;cursor:pointer;transition:background .15s;font-size:.95rem;
color:var(--ti-fg);list-style:none;display:flex;align-items:center;gap:10px;
}
.dd .dd-menu li:hover{background:var(--ti-accent-ghost)}
.dd .dd-menu li.is-selected{background:var(--ti-accent-soft);font-weight:500;color:var(--ti-accent-deep)}
.dd .dd-placeholder{color:var(--ti-muted)}
.dd .dd-check{width:18px;text-align:center;color:var(--ti-accent);flex:none}
.dd .dd-check.is-hidden{opacity:0}
.dd-search .dd-menu{padding:0}
.dd-search .dd-input{
width:100%;padding:11px 16px;border:none;border-bottom:1px solid var(--ti-line);
font-size:.95rem;outline:none;background:var(--ti-field);color:var(--ti-fg);
}
.dd-search .dd-input:focus{background:var(--ti-field-hover)}
.dd-search .dd-options{max-height:168px;overflow-y:auto;list-style:none;margin:0;padding:4px 0}
.dd-search .dd-options li.is-hidden{display:none}
.dd-search .dd-empty{padding:14px 18px;color:var(--ti-muted);font-size:.9rem;text-align:center}function ddInit(root){
var toggle = root.querySelector('.dd-toggle');
var menu = root.querySelector('.dd-menu');
if (!toggle || !menu) { return null; }
function close(){ root.classList.remove('is-open'); }
function open(){ root.classList.add('is-open'); }
function isOpen(){ return root.classList.contains('is-open'); }
toggle.addEventListener('click', function(e){
e.stopPropagation();
if (isOpen()) { close(); } else { open(); }
});
document.addEventListener('click', function(e){
if (!root.contains(e.target)) { close(); }
});
document.addEventListener('keydown', function(e){
if (e.key === 'Escape') { close(); }
});
return { root: root, toggle: toggle, menu: menu, open: open, close: close, isOpen: isOpen };
}
var dd = ddInit(document.getElementById('search'));
if (dd) {
var label = dd.root.querySelector('.dd-label');
var input = dd.root.querySelector('.dd-input');
var empty = dd.root.querySelector('.dd-empty');
var items = dd.root.querySelectorAll('.dd-options li');
dd.toggle.addEventListener('click', function(){
if (dd.isOpen()) { setTimeout(function(){ input.focus(); }, 80); }
});
input.addEventListener('click', function(e){ e.stopPropagation(); });
input.addEventListener('input', function(){
var q = input.value.toLowerCase().trim();
var found = 0;
items.forEach(function(li){
var hit = li.textContent.toLowerCase().indexOf(q) > -1;
li.classList.toggle('is-hidden', !hit);
if (hit) { found++; }
});
empty.hidden = found > 0;
});
items.forEach(function(li){
li.addEventListener('click', function(e){
e.stopPropagation();
label.textContent = li.textContent.trim();
label.classList.remove('dd-placeholder');
dd.close();
input.value = '';
items.forEach(function(x){ x.classList.remove('is-hidden'); });
empty.hidden = true;
});
});
}④分组下拉分类
Grouped Dropdown
作用
将选项按类别分组,提升长列表的浏览与定位效率。
适合场景
部门选择、分类目录、机构树等层级数据。
AGENT 提示词
生成一个分组下拉,按「技术线 / 产品线 / 运营线」分组展示岗位选项。
<div class="dd dd-grouped" id="grouped">
<button type="button" class="dd-toggle">
<span class="dd-label dd-placeholder">选择部门</span>
<i class="fas fa-chevron-down dd-arrow"></i>
</button>
<ul class="dd-menu">
<li class="dd-group">技术线</li>
<li data-value="frontend">前端开发</li>
<li data-value="backend">后端开发</li>
<li data-value="devops">运维开发</li>
<li class="dd-group">产品线</li>
<li data-value="pm">产品经理</li>
<li data-value="design">UI/UX 设计</li>
<li class="dd-group">运营线</li>
<li data-value="content">内容运营</li>
<li data-value="growth">增长运营</li>
</ul>
</div>.dd{position:relative;width:100%;min-width:190px;max-width:340px}
.dd .dd-toggle{
width:100%;padding:10px 16px;background:var(--ti-field);border:1.5px solid var(--ti-border);
border-radius:12px;font-size:.95rem;color:var(--ti-fg);cursor:pointer;display:flex;
justify-content:space-between;align-items:center;gap:8px;text-align:left;
transition:border-color .2s,background .2s;
}
.dd .dd-toggle:hover{border-color:var(--ti-accent);background:var(--ti-field-hover)}
.dd .dd-toggle .dd-arrow{transition:transform .2s;flex:none;font-size:.8rem;color:var(--ti-muted)}
.dd.is-open .dd-toggle .dd-arrow{transform:rotate(180deg)}
.dd .dd-menu{
position:absolute;top:calc(100% + 6px);left:0;width:100%;background:var(--ti-panel);
border-radius:14px;box-shadow:var(--ti-shadow);border:1px solid var(--ti-line);
opacity:0;visibility:hidden;transform:translateY(-6px);
transition:opacity .2s,transform .2s,visibility .2s;z-index:100;
list-style:none;margin:0;padding:6px 0;overflow:hidden;
}
.dd.is-open .dd-menu{opacity:1;visibility:visible;transform:translateY(0)}
.dd .dd-menu li{
padding:10px 18px;cursor:pointer;transition:background .15s;font-size:.95rem;
color:var(--ti-fg);list-style:none;display:flex;align-items:center;gap:10px;
}
.dd .dd-menu li:hover{background:var(--ti-accent-ghost)}
.dd .dd-menu li.is-selected{background:var(--ti-accent-soft);font-weight:500;color:var(--ti-accent-deep)}
.dd .dd-placeholder{color:var(--ti-muted)}
.dd .dd-check{width:18px;text-align:center;color:var(--ti-accent);flex:none}
.dd .dd-check.is-hidden{opacity:0}
.dd-grouped .dd-menu{max-height:230px;overflow-y:auto}
.dd-grouped .dd-menu li{padding-left:28px}
.dd-grouped .dd-menu li.dd-group{
padding:8px 18px 4px;font-size:.7rem;text-transform:uppercase;letter-spacing:.5px;
color:var(--ti-muted);font-weight:600;cursor:default;
}
.dd-grouped .dd-menu li.dd-group:hover{background:transparent}function ddInit(root){
var toggle = root.querySelector('.dd-toggle');
var menu = root.querySelector('.dd-menu');
if (!toggle || !menu) { return null; }
function close(){ root.classList.remove('is-open'); }
function open(){ root.classList.add('is-open'); }
function isOpen(){ return root.classList.contains('is-open'); }
toggle.addEventListener('click', function(e){
e.stopPropagation();
if (isOpen()) { close(); } else { open(); }
});
document.addEventListener('click', function(e){
if (!root.contains(e.target)) { close(); }
});
document.addEventListener('keydown', function(e){
if (e.key === 'Escape') { close(); }
});
return { root: root, toggle: toggle, menu: menu, open: open, close: close, isOpen: isOpen };
}
var dd = ddInit(document.getElementById('grouped'));
if (dd) {
var label = dd.root.querySelector('.dd-label');
dd.menu.querySelectorAll('li:not(.dd-group)').forEach(function(li){
li.addEventListener('click', function(e){
e.stopPropagation();
label.textContent = li.textContent.trim();
label.classList.remove('dd-placeholder');
dd.menu.querySelectorAll('li').forEach(function(x){ x.classList.remove('is-selected'); });
li.classList.add('is-selected');
dd.close();
});
});
}⑤级联下拉联动
Cascading Dropdown
作用
选项之间存在层级依赖,后一级随前一级选择联动更新。
适合场景
省市区选择、分类联动、地址录入等。
AGENT 提示词
生成一个两级级联下拉,选择省份后联动刷新对应城市列表。
<div class="dd dd-cascade" id="cascade">
<div class="dd-row">
<select class="dd-select" id="province">
<option value="">省份</option>
<option value="gd">广东</option>
<option value="zj">浙江</option>
<option value="sc">四川</option>
</select>
<select class="dd-select" id="city">
<option value="">城市</option>
</select>
</div>
<div class="dd-result" id="result">请先选择省份</div>
</div>.dd{position:relative;width:100%;min-width:190px;max-width:340px}
.dd .dd-toggle{
width:100%;padding:10px 16px;background:var(--ti-field);border:1.5px solid var(--ti-border);
border-radius:12px;font-size:.95rem;color:var(--ti-fg);cursor:pointer;display:flex;
justify-content:space-between;align-items:center;gap:8px;text-align:left;
transition:border-color .2s,background .2s;
}
.dd .dd-toggle:hover{border-color:var(--ti-accent);background:var(--ti-field-hover)}
.dd .dd-toggle .dd-arrow{transition:transform .2s;flex:none;font-size:.8rem;color:var(--ti-muted)}
.dd.is-open .dd-toggle .dd-arrow{transform:rotate(180deg)}
.dd .dd-menu{
position:absolute;top:calc(100% + 6px);left:0;width:100%;background:var(--ti-panel);
border-radius:14px;box-shadow:var(--ti-shadow);border:1px solid var(--ti-line);
opacity:0;visibility:hidden;transform:translateY(-6px);
transition:opacity .2s,transform .2s,visibility .2s;z-index:100;
list-style:none;margin:0;padding:6px 0;overflow:hidden;
}
.dd.is-open .dd-menu{opacity:1;visibility:visible;transform:translateY(0)}
.dd .dd-menu li{
padding:10px 18px;cursor:pointer;transition:background .15s;font-size:.95rem;
color:var(--ti-fg);list-style:none;display:flex;align-items:center;gap:10px;
}
.dd .dd-menu li:hover{background:var(--ti-accent-ghost)}
.dd .dd-menu li.is-selected{background:var(--ti-accent-soft);font-weight:500;color:var(--ti-accent-deep)}
.dd .dd-placeholder{color:var(--ti-muted)}
.dd .dd-check{width:18px;text-align:center;color:var(--ti-accent);flex:none}
.dd .dd-check.is-hidden{opacity:0}
.dd-cascade{display:flex;flex-direction:column;gap:12px;max-width:360px}
.dd-cascade .dd-row{display:flex;gap:10px;flex-wrap:wrap}
.dd-cascade .dd-select{
flex:1;min-width:110px;padding:10px 12px;background:var(--ti-field);
border:1.5px solid var(--ti-border);border-radius:12px;font-size:.9rem;
color:var(--ti-fg);outline:none;cursor:pointer;transition:border-color .2s;
}
.dd-cascade .dd-select:hover{border-color:var(--ti-accent)}
.dd-cascade .dd-select:focus{border-color:var(--ti-accent-deep)}
.dd-cascade .dd-result{font-size:.85rem;color:var(--ti-muted)}var province = document.getElementById('province');
var city = document.getElementById('city');
var result = document.getElementById('result');
var MAP = {
gd: { name: '广东', cities: ['广州', '深圳', '珠海'] },
zj: { name: '浙江', cities: ['杭州', '宁波', '温州'] },
sc: { name: '四川', cities: ['成都', '绵阳', '乐山'] }
};
province.addEventListener('change', function(){
var val = province.value;
city.innerHTML = '<option value="">城市</option>';
if (val && MAP[val]) {
MAP[val].cities.forEach(function(c){
var opt = document.createElement('option');
opt.value = c;
opt.textContent = c;
city.appendChild(opt);
});
result.textContent = '已选省份:' + MAP[val].name + ',请继续选择城市';
} else {
result.textContent = '请先选择省份';
}
});
city.addEventListener('change', function(){
var p = province.value;
if (city.value && p && MAP[p]) {
result.textContent = '当前选择:' + MAP[p].name + ' / ' + city.value;
}
});⑥标签下拉多选 + 标签
Tag Dropdown
作用
以彩色标签展示已选项,强调视觉反馈,适合快速添加与移除。
适合场景
技能选择、兴趣标签、多维度筛选等。
AGENT 提示词
生成一个标签下拉,已选技能以彩色标签展示,支持点击移除。
<div class="dd dd-tags" id="tags">
<div class="dd-toggle" role="button" tabindex="0">
<span class="dd-pills"><span class="dd-placeholder">选择技能</span></span>
<i class="fas fa-chevron-down dd-arrow"></i>
</div>
<ul class="dd-menu">
<li data-value="js"><span class="dd-check is-hidden">✓</span> JavaScript</li>
<li data-value="ts"><span class="dd-check is-hidden">✓</span> TypeScript</li>
<li data-value="py"><span class="dd-check is-hidden">✓</span> Python</li>
<li data-value="rust"><span class="dd-check is-hidden">✓</span> Rust</li>
<li data-value="go"><span class="dd-check is-hidden">✓</span> Go</li>
</ul>
</div>.dd{position:relative;width:100%;min-width:190px;max-width:340px}
.dd .dd-toggle{
width:100%;padding:10px 16px;background:var(--ti-field);border:1.5px solid var(--ti-border);
border-radius:12px;font-size:.95rem;color:var(--ti-fg);cursor:pointer;display:flex;
justify-content:space-between;align-items:center;gap:8px;text-align:left;
transition:border-color .2s,background .2s;
}
.dd .dd-toggle:hover{border-color:var(--ti-accent);background:var(--ti-field-hover)}
.dd .dd-toggle .dd-arrow{transition:transform .2s;flex:none;font-size:.8rem;color:var(--ti-muted)}
.dd.is-open .dd-toggle .dd-arrow{transform:rotate(180deg)}
.dd .dd-menu{
position:absolute;top:calc(100% + 6px);left:0;width:100%;background:var(--ti-panel);
border-radius:14px;box-shadow:var(--ti-shadow);border:1px solid var(--ti-line);
opacity:0;visibility:hidden;transform:translateY(-6px);
transition:opacity .2s,transform .2s,visibility .2s;z-index:100;
list-style:none;margin:0;padding:6px 0;overflow:hidden;
}
.dd.is-open .dd-menu{opacity:1;visibility:visible;transform:translateY(0)}
.dd .dd-menu li{
padding:10px 18px;cursor:pointer;transition:background .15s;font-size:.95rem;
color:var(--ti-fg);list-style:none;display:flex;align-items:center;gap:10px;
}
.dd .dd-menu li:hover{background:var(--ti-accent-ghost)}
.dd .dd-menu li.is-selected{background:var(--ti-accent-soft);font-weight:500;color:var(--ti-accent-deep)}
.dd .dd-placeholder{color:var(--ti-muted)}
.dd .dd-check{width:18px;text-align:center;color:var(--ti-accent);flex:none}
.dd .dd-check.is-hidden{opacity:0}
.dd-tags .dd-toggle{min-height:46px;flex-wrap:wrap}
.dd-tags .dd-pills{display:flex;flex-wrap:wrap;gap:5px;flex:1}
.dd-tags .dd-pill{
background:var(--ti-accent);color:#fff;padding:3px 9px 3px 13px;border-radius:30px;
font-size:.78rem;font-weight:500;display:inline-flex;align-items:center;gap:6px;
}
.dd-tags .dd-pill i{cursor:pointer;opacity:.85;font-size:.7rem;transition:opacity .2s}
.dd-tags .dd-pill i:hover{opacity:1}
.dd-tags .dd-menu{max-height:200px;overflow-y:auto}function ddInit(root){
var toggle = root.querySelector('.dd-toggle');
var menu = root.querySelector('.dd-menu');
if (!toggle || !menu) { return null; }
function close(){ root.classList.remove('is-open'); }
function open(){ root.classList.add('is-open'); }
function isOpen(){ return root.classList.contains('is-open'); }
toggle.addEventListener('click', function(e){
e.stopPropagation();
if (isOpen()) { close(); } else { open(); }
});
document.addEventListener('click', function(e){
if (!root.contains(e.target)) { close(); }
});
document.addEventListener('keydown', function(e){
if (e.key === 'Escape') { close(); }
});
return { root: root, toggle: toggle, menu: menu, open: open, close: close, isOpen: isOpen };
}
var dd = ddInit(document.getElementById('tags'));
if (dd) {
var box = dd.root.querySelector('.dd-pills');
var picked = [];
function labelOf(val){
var li = dd.menu.querySelector('li[data-value="' + val + '"]');
return li ? li.textContent.trim() : val;
}
function render(){
if (!picked.length) {
box.innerHTML = '<span class="dd-placeholder">选择技能</span>';
return;
}
box.innerHTML = picked.map(function(v){
return '<span class="dd-pill">' + labelOf(v) +
' <i class="fas fa-times" data-value="' + v + '"></i></span>';
}).join('');
box.querySelectorAll('.dd-pill i').forEach(function(icon){
icon.addEventListener('click', function(e){
e.stopPropagation();
toggleVal(icon.getAttribute('data-value'));
});
});
}
function toggleVal(val){
var i = picked.indexOf(val);
var li = dd.menu.querySelector('li[data-value="' + val + '"]');
var check = li ? li.querySelector('.dd-check') : null;
if (i > -1) {
picked.splice(i, 1);
if (check) { check.classList.add('is-hidden'); }
} else {
picked.push(val);
if (check) { check.classList.remove('is-hidden'); }
}
render();
}
dd.menu.querySelectorAll('li').forEach(function(li){
li.addEventListener('click', function(e){
e.stopPropagation();
toggleVal(li.getAttribute('data-value'));
});
});
}⑦自定义下拉品牌定制
Custom Dropdown
作用
完全定制化样式与交互动效,满足品牌调性与特殊需求。
适合场景
用户菜单、品牌入口、特殊交互等。
AGENT 提示词
生成一个自定义风格的用户菜单下拉,带品牌色图标与展开动效。
<div class="dd dd-custom" id="custom">
<button type="button" class="dd-toggle">
<span class="dd-face">
<span class="dd-icon"><i class="fas fa-user"></i></span>
<span class="dd-label">用户菜单</span>
</span>
<i class="fas fa-chevron-down dd-arrow"></i>
</button>
<ul class="dd-menu">
<li data-value="profile"><i class="fas fa-user-circle"></i> 个人中心</li>
<li data-value="setting"><i class="fas fa-cog"></i> 设置</li>
<li data-value="star"><i class="fas fa-star"></i> 收藏</li>
<li data-value="logout"><i class="fas fa-sign-out-alt"></i> 退出</li>
</ul>
</div>.dd{position:relative;width:100%;min-width:190px;max-width:340px}
.dd .dd-toggle{
width:100%;padding:10px 16px;background:var(--ti-field);border:1.5px solid var(--ti-border);
border-radius:12px;font-size:.95rem;color:var(--ti-fg);cursor:pointer;display:flex;
justify-content:space-between;align-items:center;gap:8px;text-align:left;
transition:border-color .2s,background .2s;
}
.dd .dd-toggle:hover{border-color:var(--ti-accent);background:var(--ti-field-hover)}
.dd .dd-toggle .dd-arrow{transition:transform .2s;flex:none;font-size:.8rem;color:var(--ti-muted)}
.dd.is-open .dd-toggle .dd-arrow{transform:rotate(180deg)}
.dd .dd-menu{
position:absolute;top:calc(100% + 6px);left:0;width:100%;background:var(--ti-panel);
border-radius:14px;box-shadow:var(--ti-shadow);border:1px solid var(--ti-line);
opacity:0;visibility:hidden;transform:translateY(-6px);
transition:opacity .2s,transform .2s,visibility .2s;z-index:100;
list-style:none;margin:0;padding:6px 0;overflow:hidden;
}
.dd.is-open .dd-menu{opacity:1;visibility:visible;transform:translateY(0)}
.dd .dd-menu li{
padding:10px 18px;cursor:pointer;transition:background .15s;font-size:.95rem;
color:var(--ti-fg);list-style:none;display:flex;align-items:center;gap:10px;
}
.dd .dd-menu li:hover{background:var(--ti-accent-ghost)}
.dd .dd-menu li.is-selected{background:var(--ti-accent-soft);font-weight:500;color:var(--ti-accent-deep)}
.dd .dd-placeholder{color:var(--ti-muted)}
.dd .dd-check{width:18px;text-align:center;color:var(--ti-accent);flex:none}
.dd .dd-check.is-hidden{opacity:0}
.dd-custom .dd-toggle{
border-radius:40px;border-width:2px;font-weight:500;
box-shadow:0 2px 8px rgba(84,164,243,.1);
}
.dd-custom .dd-toggle:hover{
border-color:var(--ti-accent);box-shadow:0 4px 16px rgba(84,164,243,.22);
}
.dd-custom .dd-face{display:inline-flex;align-items:center;gap:10px}
.dd-custom .dd-icon{
background:var(--ti-accent);color:#fff;border-radius:50%;width:26px;height:26px;
display:inline-flex;align-items:center;justify-content:center;font-size:.8rem;flex:none;
}
.dd-custom .dd-menu{border-radius:22px;padding:8px 0;transform:translateY(-8px) scale(.96)}
.dd-custom.is-open .dd-menu{transform:translateY(0) scale(1)}
.dd-custom .dd-menu li{
padding:12px 20px;gap:12px;border-left:3px solid transparent;
transition:background .15s,padding-left .2s,border-color .2s;
}
.dd-custom .dd-menu li:hover{
background:var(--ti-accent-ghost);border-left-color:var(--ti-accent);padding-left:26px;
}
.dd-custom .dd-menu li i{width:22px;color:var(--ti-accent);flex:none}function ddInit(root){
var toggle = root.querySelector('.dd-toggle');
var menu = root.querySelector('.dd-menu');
if (!toggle || !menu) { return null; }
function close(){ root.classList.remove('is-open'); }
function open(){ root.classList.add('is-open'); }
function isOpen(){ return root.classList.contains('is-open'); }
toggle.addEventListener('click', function(e){
e.stopPropagation();
if (isOpen()) { close(); } else { open(); }
});
document.addEventListener('click', function(e){
if (!root.contains(e.target)) { close(); }
});
document.addEventListener('keydown', function(e){
if (e.key === 'Escape') { close(); }
});
return { root: root, toggle: toggle, menu: menu, open: open, close: close, isOpen: isOpen };
}
var dd = ddInit(document.getElementById('custom'));
if (dd) {
var label = dd.root.querySelector('.dd-label');
var icon = dd.root.querySelector('.dd-icon');
dd.menu.querySelectorAll('li').forEach(function(li){
li.addEventListener('click', function(e){
e.stopPropagation();
label.textContent = li.textContent.trim();
var picked = li.querySelector('i');
if (picked && icon) { icon.innerHTML = '<i class="' + picked.className + '"></i>'; }
dd.menu.querySelectorAll('li').forEach(function(x){ x.classList.remove('is-selected'); });
li.classList.add('is-selected');
dd.close();
});
});
}
评论 (0)