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

移动端Chip组件展开/更多功能实现咨询(无第三方UI库)

解决方案

核心思路

放弃单纯依赖CSS flex + overflow:hidden 的方案,改用JS动态计算可视区域可容纳的Chip数量,主动隐藏超出部分,并在末尾显示「+N more」按钮,点击按钮弹出底部面板展示所有选中项。

实现代码

HTML结构

<!-- Autocomplete 输入框 -->
<input type="text" class="autocomplete-input" placeholder="输入选项">

<!-- Chip 展示容器 -->
<div class="chip-container">
  <!-- 可视Chip容器 -->
  <div class="visible-chips"></div>
  <!-- 剩余Chip容器(默认隐藏) -->
  <div class="hidden-chips"></div>
  <!-- More按钮(默认隐藏) -->
  <button class="more-btn">+<span class="more-count">0</span> more</button>
</div>

<!-- 底部面板(默认隐藏) -->
<div class="bottom-panel">
  <div class="panel-header">
    <h3>所有选中项</h3>
    <button class="close-panel">×</button>
  </div>
  <div class="panel-chips"></div>
</div>
<!-- 遮罩层 -->
<div class="overlay"></div>

CSS样式

/* 全局基础样式 */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: sans-serif;
}

/* Chip容器 */
.chip-container {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  padding: 8px;
  border: 1px solid #eee;
  border-radius: 4px;
  margin-top: 8px;
  overflow: visible; /* 取消overflow:hidden,避免裁剪more按钮 */
}

/* 单个Chip样式 */
.chip {
  display: inline-flex;
  align-items: center;
  padding: 4px 8px;
  background: #e0e0e0;
  border-radius: 16px;
  font-size: 14px;
  gap: 4px;
}

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

/* 可视Chip容器 */
.visible-chips {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
}

/* 剩余Chip容器 */
.hidden-chips {
  display: none;
}

/* More按钮 */
.more-btn {
  display: none;
  padding: 4px 8px;
  border: none;
  background: #f5f5f5;
  border-radius: 16px;
  cursor: pointer;
  font-size: 14px;
}

/* 移动端适配(以768px为分界) */
@media (max-width: 768px) {
  .chip-container {
    flex-wrap: nowrap;
    overflow: visible;
    min-width: 0;
  }
  
  .visible-chips {
    flex-wrap: nowrap;
    overflow: hidden;
    min-width: 0;
  }
}

/* 底部面板样式 */
.bottom-panel {
  position: fixed;
  bottom: -100%;
  left: 0;
  width: 100%;
  background: #fff;
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
  transition: bottom 0.3s ease;
  max-height: 70vh;
  overflow-y: auto;
}

.bottom-panel.open {
  bottom: 0;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px;
  border-bottom: 1px solid #eee;
}

.close-panel {
  border: none;
  background: none;
  font-size: 24px;
  cursor: pointer;
}

.panel-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 16px;
}

/* 遮罩层 */
.overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.5);
  display: none;
}

.overlay.open {
  display: block;
}

JavaScript逻辑

// 模拟选中的Chip数据(最多10个)
let selectedChips = ['选项1', '选项2', '选项3', '选项4', '选项5', '选项6', '选项7'];

// DOM元素
const visibleChipsEl = document.querySelector('.visible-chips');
const hiddenChipsEl = document.querySelector('.hidden-chips');
const moreBtnEl = document.querySelector('.more-btn');
const moreCountEl = document.querySelector('.more-count');
const bottomPanelEl = document.querySelector('.bottom-panel');
const panelChipsEl = document.querySelector('.panel-chips');
const overlayEl = document.querySelector('.overlay');
const closePanelBtn = document.querySelector('.close-panel');

// 初始化渲染
renderChips();
// 窗口 resize 时重新计算
window.addEventListener('resize', renderChips);

// 渲染Chip函数
function renderChips() {
  // 清空容器
  visibleChipsEl.innerHTML = '';
  hiddenChipsEl.innerHTML = '';
  panelChipsEl.innerHTML = '';

  // 生成所有Chip的DOM
  const chipElements = selectedChips.map(text => createChipElement(text));
  
  // 渲染面板内的所有Chip
  chipElements.forEach(el => panelChipsEl.appendChild(el.cloneNode(true)));

  // 移动端计算可视数量
  if (window.innerWidth <= 768) {
    const containerWidth = document.querySelector('.chip-container').offsetWidth;
    const moreBtnWidth = moreBtnEl.offsetWidth;
    let totalWidth = 0;
    let visibleCount = 0;

    // 计算能容纳的Chip数量
    for (let i = 0; i < chipElements.length; i++) {
      const chipWidth = chipElements[i].offsetWidth;
      // 预留间隔和more按钮的宽度
      if (totalWidth + chipWidth + 8 + moreBtnWidth <= containerWidth) {
        totalWidth += chipWidth + 8;
        visibleChipsEl.appendChild(chipElements[i]);
        visibleCount++;
      } else {
        hiddenChipsEl.appendChild(chipElements[i]);
      }
    }

    // 显示/隐藏more按钮
    const hiddenCount = chipElements.length - visibleCount;
    if (hiddenCount > 0) {
      moreBtnEl.style.display = 'block';
      moreCountEl.textContent = hiddenCount;
    } else {
      moreBtnEl.style.display = 'none';
    }
  } else {
    // 桌面端显示所有Chip(最多10个)
    chipElements.forEach(el => visibleChipsEl.appendChild(el));
    moreBtnEl.style.display = 'none';
  }
}

// 创建单个Chip元素
function createChipElement(text) {
  const chip = document.createElement('div');
  chip.className = 'chip';
  chip.innerHTML = `
    <span>${text}</span>
    <span class="remove-btn">×</span>
  `;
  // 绑定删除事件
  chip.querySelector('.remove-btn').addEventListener('click', () => {
    selectedChips = selectedChips.filter(item => item !== text);
    renderChips();
  });
  return chip;
}

// 打开底部面板
moreBtnEl.addEventListener('click', () => {
  bottomPanelEl.classList.add('open');
  overlayEl.classList.add('open');
});

// 关闭底部面板
function closePanel() {
  bottomPanelEl.classList.remove('open');
  overlayEl.classList.remove('open');
}
closePanelBtn.addEventListener('click', closePanel);
overlayEl.addEventListener('click', closePanel);

关键说明

  1. 避免裁剪问题:取消了Chip容器的overflow:hidden,改用给visible-chips容器设置overflow:hidden,确保more按钮始终能完整显示。
  2. 动态计算适配:通过JS获取容器宽度、more按钮宽度和每个Chip的宽度,精确计算可视区域能容纳的Chip数量,剩余的放入隐藏容器。
  3. 交互逻辑:点击more按钮弹出底部面板展示所有选中项,点击遮罩或关闭按钮收起面板,同时支持Chip的删除操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 19:35:36