
本文介绍一种基于 data-filter 属性与现代 DOM API 的响应式筛选方案,避免为每个元素手动增删 active/hidden 类,大幅提升代码可维护性与扩展性。
本文介绍一种基于 `data-filter` 属性与现代 dom api 的响应式筛选方案,避免为每个元素手动增删 `active`/`hidden` 类,大幅提升代码可维护性与扩展性。
在构建多分类内容筛选功能(如按“早餐”“午餐”“饮品”过滤 8 个食品区块)时,初学者常采用硬编码方式——为每个 DOM 元素单独调用 classList.add() 和 classList.remove()。这种方式虽能运行,但严重违反 DRY(Don’t Repeat Yourself)原则:新增一个食品项就得补 2 行代码;按钮逻辑变更需同步修改多处;可读性差、易出错、难以测试。
更专业、可持续的解法是语义化标记 + 批量声明式操作。核心思路如下:
- 统一标识筛选维度:为所有可筛选元素(.filter > div)添加 data-filter 属性,值为逗号分隔的类别标签(如 "breakfast"、"breakfast,lunch"),支持多标签匹配;
- 绑定筛选控制权:为按钮统一添加 data-filter(如 "all"、"lunch"),并集中注册事件监听器;
- 批量响应状态切换:利用 document.querySelectorAll() 获取目标集合,结合 forEach() 与 toggle() 实现原子化状态更新。
以下是精简可靠的实现代码:
<!-- 筛选按钮组 --> <div class="filter-buttons"> <button class="active" data-filter="all">全部</button> <button data-filter="breakfast">早餐</button> <button data-filter="lunch">午餐</button> <button data-filter="drinks">饮品</button> </div> <!-- 可筛选内容区 --> <div class="filter"> <div data-filter="breakfast">燕麦粥</div> <div data-filter="breakfast,lunch">三明治</div> <div data-filter="drinks">橙汁</div> <div data-filter="lunch">咖喱饭</div> <div data-filter="drinks">冰咖啡</div> <div data-filter="breakfast">酸奶杯</div> <div data-filter="lunch,drinks">沙拉+柠檬水</div> <div data-filter="breakfast">煎蛋卷</div> </div>
/* 样式层:仅控制显隐与视觉反馈 */
.filter-buttons {
display: flex; gap: 8px; margin-bottom: 16px;
}
.filter-buttons button.active {
background-color: #007bff; color: white; border: none;
}
.filter > div:not(.active) {
display: none;
}
// 逻辑层:单次注册,全量响应
document.querySelectorAll('.filter-buttons button').forEach(button => {
button.addEventListener('click', function() {
// 1. 同步按钮激活态
document.querySelectorAll('.filter-buttons button').forEach(el => el.classList.remove('active'));
this.classList.add('active');
// 2. 获取当前筛选类型
const filterType = this.dataset.filter;
// 3. 批量处理内容项
document.querySelectorAll('.filter > div').forEach(item => {
if (filterType === 'all') {
item.classList.add('active');
} else {
const itemFilters = item.dataset.filter.split(',').map(s => s.trim());
item.classList.toggle('active', itemFilters.includes(filterType));
}
});
});
});
✅ 关键优势说明:
-
零冗余:无论增加第 9 个还是第 50 个食品项,HTML 中只需添加一行 ,JS 与 CSS 完全无需改动;
- 支持复合标签:如 data-filter="breakfast,lunch" 可同时被“早餐”和“午餐”按钮激活,灵活应对业务复杂度;
- 状态隔离清晰:按钮激活态(.filter-buttons button.active)与内容显隐态(.filter > div.active)职责分离,便于调试与样式覆盖;
- 可扩展性强:后续增加“晚餐”按钮?只需新增 即可生效。
⚠️ 注意事项:
- 确保所有 .filter > div 初始不带 active 类(由 JS 统一控制),避免样式冲突;
- data-filter 值建议小写、无空格(或统一 trim() 处理),保障匹配可靠性;
- 若需动画效果,可将 display: none 替换为 opacity: 0; visibility: hidden; 并配合 CSS 过渡。
这种模式不仅适用于食品筛选,还可无缝迁移至商品列表、博客标签页、项目看板等任意分类展示场景——让代码真正服务于逻辑,而非沦为 DOM 操作的体力活。










