筛选区与内容区必须分层,外层.container下首层.row放筛选控件、次层.row放网格内容;筛选控件需响应式且禁用g-3;jQuery筛选须用.filter()而非.hide()/.show();图标/卡片网格须直接以col-*包裹内容,禁用中间wrapper;data-属性须全小写短横线分隔。

筛选区域必须用 .row + .col-* 套在网格容器外层,不能塞进 .row 里面
常见错误是把搜索框、分类下拉这些筛选控件直接放进产品列表的 .row 里,结果一刷新页面,筛选区和列表错位、断点失效、小屏下换行混乱。筛选区和内容区逻辑上是两个独立区块,必须分层。
- 正确结构:外层一个
.container→ 下面第一层.row放筛选控件(比如.col-12 col-md-6 col-lg-4包搜索框)→ 第二层再起一个.row放产品或图标网格 - 筛选控件本身也得响应式:搜索框用
.form-control+.w-100,下拉用.form-select,按钮加.btn-sm避免小屏撑开列宽 - 别给筛选区加
g-3—— 它只对列内间距有效;水平对齐用justify-content-between或text-center更稳
data-filter 属性 + jQuery 筛选时,必须用 .filter() 而非 .hide()/.show()
直接操作 display: none 会破坏 Bootstrap 网格的 flex 流,尤其在 col-md-4 这类固定列数布局下,隐藏某几项后剩余项不会自动重排成完整行,留出大片空白。
- 推荐写法:
$(".product-item").hide().filter("[data-category='shoes']").show()→ 先统一隐藏,再批量显示匹配项,保证 DOM 顺序和 flex 排列不中断 - 如果用
.addClass("d-none"),记得配套加.removeClass("d-none")清除旧状态,否则多次筛选后残留类名导致漏显 - 筛选后调用
$(".row").css("flex-wrap", "wrap")是多余动作——Bootstrap 的.row默认就是flex-wrap: wrap,无需重设
图标/卡片网格必须用 col-* 直接包裹内容,禁用中间层 div 容器
很多人为了“语义清晰”,给每个图标或卡片包一层 <div class="icon-wrapper"> 或 <div class="card-wrapper">,再把这层 div 加 col-md-4。这会导致实际参与网格计算的是 wrapper,不是内容本身,小屏下容易因 wrapper 的 padding/margin 参差不齐而换行错位。
- 图标网格:直接
<div class="col-md-4"><i class="bi bi-heart fs-2"></i><div class="small">爱心</div></div> - 产品卡片:直接
<div class="col-md-4"><div class="card h-100">...</div></div>,别套额外 div - 图片务必加
img-fluid,且不要设height—— 固定高度会破坏等比缩放,导致不同商品图在同列中高度不一致,拉扯整行高度
移动端筛选后列表空白?检查 data- 属性是否全小写且无空格
jQuery 的属性选择器对大小写敏感,[data-category="Shoes"] 和 [data-category="shoes"] 不匹配;带空格如 data-category="wireless earbuds" 会被浏览器解析为两个属性,导致筛选失效。
- 统一约定:所有
data-值用短横线分隔小写,如data-category="wireless-earbuds" - 调试时在控制台运行
$("[data-category]").length,看是否等于预期项数;再跑$("[data-category='xxx']").length验证匹配逻辑 - 如果用中文值(如
data-tag="新品"),确保 HTML 文件编码为 UTF-8,且引号闭合无 BOM 干扰


















