
本文详解如何使用 Isotope.js 同时支持多个独立筛选维度(如 type 和 tag),通过维护动态过滤器对象、正确拼接 CSS 选择器并调用 arrange({ filter: ... }),实现「勾选即显示」的多条件并集筛选效果。
本文详解如何使用 isotope.js 同时支持多个独立筛选维度(如 `type` 和 `tag`),通过维护动态过滤器对象、正确拼接 css 选择器并调用 `arrange({ filter: ... })`,实现「勾选即显示」的多条件并集筛选效果。
Isotope.js 原生支持单维度筛选(如 .blogs-and-news),但默认不直接处理跨分组的多维并集筛选(例如:同时显示所有 .blogs-and-news 或 .case-study 且 .pdf 的项目)。关键在于:不能简单用字符串拼接覆盖旧值,而需为每个筛选组(type/tag)维护独立的数组,并最终生成符合 Isotope 多类选择器语法的合并字符串(如 .blogs-and-news, .case-study, .pdf)。
✅ 正确实现逻辑
- 按组管理筛选状态:使用对象 filters = { type: [], tag: [] } 存储各维度当前激活的类名;
- 点击时智能增删:检查 <li> 是否已添加 selected 类 —— 若有,则将对应 data-filter 值推入该组数组;若无,则从数组中移除该值;
- 生成并集选择器:将所有组内数组扁平化后,用 , 连接(如 ['.blogs-and-news', '.case-study', '.pdf'] → '.blogs-and-news, .case-study, .pdf');
- 触发重排:调用 iso.arrange({ filter: selectorString })。
⚠️ 注意:原代码中 data-filter-group="type" 在「标签」区域被误设为 type(应为 tag),务必修正 HTML 结构,否则分组逻辑失效。
✅ 完整可运行示例代码
<!-- 确保 data-filter-group 属性准确区分维度 -->
<div class="rSidebar__box">
<span class="rSidebar__label">Filter by tag</span>
<ul class="rSidebar__options" data-filter-group="tag"> <!-- ✅ 改为 "tag" -->
<li class="rSidebar__options-li" data-filter=".pdf">PDF</li>
<li class="rSidebar__options-li" data-filter=".article">Article</li>
</ul>
</div>
<div class="rSidebar__box">
<span class="rSidebar__label">Filter by type</span>
<ul class="rSidebar__options" data-filter-group="type"> <!-- ✅ 保持 "type" -->
<li class="rSidebar__options-li" data-filter=".blogs-and-news">Blog & News</li>
<li class="rSidebar__options-li" data-filter=".case-study">Case Studies</li>
</ul>
</div>document.addEventListener('DOMContentLoaded', () => {
const container = document.querySelector('.grid');
const iso = new Isotope(container, {
itemSelector: '.resourceCard',
layoutMode: 'fitRows',
transitionDuration: '0.5s'
});
const filters = {}; // 动态存储:{ type: ['.blogs-and-news'], tag: ['.pdf'] }
const optionLinks = document.querySelectorAll('.rSidebar__options-li');
// 将多维数组扁平化并拼接为 Isotope 兼容的选择器字符串
function concatValues(obj) {
const allValues = [];
for (const group in obj) {
if (Array.isArray(obj[group])) {
allValues.push(...obj[group]);
}
}
return allValues.join(', ') || '*'; // 若无筛选项,显示全部('*' 是 Isotope 通配符)
}
function handleFilterClick(el) {
const group = el.closest('.rSidebar__options').getAttribute('data-filter-group');
const value = el.getAttribute('data-filter');
// 初始化该组数组(若不存在)
if (!filters[group]) filters[group] = [];
if (el.classList.contains('selected')) {
// 已选中 → 加入该组
filters[group].push(value);
} else {
// 取消选中 → 从该组移除
filters[group] = filters[group].filter(v => v !== value);
}
const selector = concatValues(filters);
console.log('Active filters:', selector); // 调试用
iso.arrange({ filter: selector });
}
optionLinks.forEach(el => {
el.addEventListener('click', e => {
e.preventDefault();
el.classList.toggle('selected');
handleFilterClick(el);
});
});
});? 关键注意事项
- HTML 分组属性必须唯一且准确:data-filter-group="tag" 和 data-filter-group="type" 不可混淆,否则筛选逻辑错乱;
- filter 值是 CSS 选择器字符串:Isotope 的 filter 选项接受 .class1, .class2, .class3 形式的并集选择器,不是数组也不是对象;
- 避免重复添加相同值:利用 classList.contains('selected') 判断状态,而非仅依赖事件触发顺序;
- 空筛选时显示全部:concatValues() 返回空字符串时,应 fallback 为 '*',否则 Isotope 可能隐藏所有元素;
- 性能提示:对于大量数据,可考虑防抖 arrange() 调用,但本例中无需。
通过以上结构化实现,即可轻松支持任意数量的独立筛选维度,并确保用户每次勾选/取消均实时、准确地更新视图 —— 真正实现灵活、可扩展的多维内容筛选体验。


















