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

CSS选择器实现同部分类名分组首尾元素识别与分组边框绘制

问题原因

你之前使用的:first-child、:last-child选择器,匹配的是父元素下全局的第一个/最后一个子元素,而非相同bundle类名分组的首尾元素,所以无法给中间的bundle-2分组正确加上下边框。
纯CSS原生没有提供「自动识别相邻相同类名元素分组」的能力,可以根据你的场景选择以下三种方案实现需求:

方案1:调整DOM结构(推荐,兼容性最好)

这是最符合语义、适配所有浏览器的实现方式:把同属一个捆绑包的产品嵌套在同一个分组容器内,直接给容器加边框即可。由于<ul>的直接子元素只能是<li>,推荐按如下结构调整:

<ul>
  <li class="bundle-group">
    <ul>
      <li>Product 1</li>
      <li>Product 2</li>
    </ul>
  </li>
  <li class="bundle-group">
    <ul>
      <li>Product A</li>
      <li>Product B</li>
    </ul>
  </li>
  <li>Product I</li>
</ul>

对应CSS代码:

/* 清除默认列表样式 */
ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.bundle-group {
  border: 1px solid blue;
  border-radius: 4px; /* 可选:给分组加圆角 */
  margin-bottom: 8px; /* 可选:分组之间加间距 */
}
.bundle-group > ul > li {
  padding: 8px 12px;
}

方案2:不修改DOM,纯CSS实现

如果不能调整现有DOM结构,分两种场景处理:

2.1 捆绑包类名固定(可提前枚举)

如果你的bundle编号是固定的(比如只有bundle-1、bundle-2),可以通过相邻兄弟选择器精确匹配每个分组的首尾:

/* 清除默认列表样式 */
ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
/* 所有捆绑包产品默认加左右边框、内边距 */
[class*="bundle-"] {
  border-left: 1px solid blue;
  border-right: 1px solid blue;
  padding: 8px 12px;
}
/* ---------- bundle-1分组样式 ---------- */
/* 分组第一个元素:是父元素第一个子元素,或前一个元素不是bundle-1 */
.bundle-1:first-child,
:not(.bundle-1) + .bundle-1 {
  border-top: 1px solid blue;
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
}
/* 分组最后一个元素:是父元素最后一个子元素,或后一个元素不是bundle-1 */
.bundle-1:last-child,
.bundle-1:has(+ :not(.bundle-1)) {
  border-bottom: 1px solid blue;
  border-bottom-left-radius: 4px;
  border-bottom-right-radius: 4px;
}
/* ---------- bundle-2分组样式,有多少个捆绑包就重复多少段 ---------- */
:not(.bundle-2) + .bundle-2 {
  border-top: 1px solid blue;
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
}
.bundle-2:last-child,
.bundle-2:has(+ :not(.bundle-2)) {
  border-bottom: 1px solid blue;
  border-bottom-left-radius: 4px;
  border-bottom-right-radius: 4px;
}

注::has()选择器支持Chrome 105+、Firefox 121+、Safari 15.4+版本,如果需要兼容更老旧的浏览器,可以去掉圆角,用边框重叠的方式实现,不需要:has():

[class*="bundle-"] {
  border: 1px solid blue;
  margin-bottom: -1px; /* 重叠同组相邻元素的边框,避免双边框 */
}
/* 不同捆绑包相邻时,增加1px间距拆分两个分组的边框 */
.bundle-1 + .bundle-2 {
  margin-top: 1px;
}

2.2 捆绑包类名动态生成(无法提前枚举)

如果bundle编号是后端动态生成、无法提前枚举,纯CSS没有能力区分相邻两个带bundle-前缀的元素是否属于同一分组(CSS不支持类名字符串比较逻辑),这种场景推荐使用方案1调整结构,或者用少量JS辅助实现。

方案3:少量JS辅助(兼容所有浏览器,适配动态类名)

如果既不想修改DOM,又要适配动态bundle编号、兼容所有浏览器,可以加几行JS自动给每个分组的首尾元素添加标记类,再写对应样式即可:

const bundleItems = document.querySelectorAll('li[class*="bundle-"]');
let currentBundleClass = '';
bundleItems.forEach((item, index) => {
  // 找到当前元素的bundle-*类名
  const itemBundleClass = Array.from(item.classList).find(className => className.startsWith('bundle-'));
  // 标记分组第一个元素
  if (itemBundleClass !== currentBundleClass) {
    item.classList.add('bundle-first');
    currentBundleClass = itemBundleClass;
  }
  // 标记分组最后一个元素
  const nextItem = bundleItems[index + 1];
  if (!nextItem || !nextItem.classList.contains(itemBundleClass)) {
    item.classList.add('bundle-last');
  }
})

对应CSS代码:

ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
[class*="bundle-"] {
  border-left: 1px solid blue;
  border-right: 1px solid blue;
  padding: 8px 12px;
}
.bundle-first {
  border-top: 1px solid blue;
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
}
.bundle-last {
  border-bottom: 1px solid blue;
  border-bottom-left-radius: 4px;
  border-bottom-right-radius: 4px;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 19:45:31