如何在Isotope.js中实现多组筛选条件的组合筛选?
问题:Isotope.js 双维度筛选失效,同一组多选无法展示所有匹配内容
我有两个独立筛选维度:
tag(PDF、Article)type(Blog & News、Case Studies)
尝试用Isotope.js实现双筛选布局,但当前总是最后点击的筛选条件优先级最高,组合筛选无法正常工作。具体需求:
- 点击「Blog & News」应显示2篇文章(当前此功能正常)
- 再点击「Case Studies」后,应同时展示案例研究和博客类文章,即同一筛选组内选中的所有选项对应的内容都要显示
- 不同筛选组之间为“且”关系,比如选中「PDF」和「Case Studies」时,仅显示同时符合这两个条件的内容
官方文档说明arrange()方法支持多筛选实例,但我的场景中无效;尝试过concatValues()拼接筛选值,也未得到正确结果。
核心问题分析
- HTML标识错误:两个筛选组的
data-filter-group都设置为type,导致Isotope无法区分不同筛选维度 - 筛选逻辑缺陷:原逻辑中同一筛选组只能单选,无法支持同一组内多选后展示所有匹配项
- 筛选器字符串生成错误:未按照Isotope的语法规则生成正确的组合筛选器(同一组内用
逗号表示“或”,不同组间用空格表示“且”)
解决方案
1. 修正HTML的筛选组标识
将第一个筛选组(Filter by tag)的data-filter-group改为tag,确保两个维度的标识唯一:
<!-- tags --> <div class="rSidebar__box"> <span class="rSidebar__label d-block fw-bold">Filter by tag</span> <ul class="rSidebar__options button-group" data-filter-group="tag"> <li class="rSidebar__options-li" data-filter=".pdf"> <span class="rSidebar__options-square"></span> <span class="rSidebar__options-label d-block buttonTemp" data-filter=".pdf">PDF</span> </li> <li class="rSidebar__options-li" data-filter=".article"> <span class="rSidebar__options-square"></span> <span class="rSidebar__options-label d-block buttonTemp" data-filter=".article">Article</span> </li> </ul> </div> <!-- type --> <div class="rSidebar__box"> <span class="rSidebar__label d-block fw-bold">Filter by type</span> <ul class="rSidebar__options button-group" data-filter-group="type"> <li class="rSidebar__options-li" data-filter=".blogs-and-news"> <span class="rSidebar__options-square"></span> <span class="rSidebar__options-label d-block buttonTemp" data-filter=".blogs-and-news">Blog & News</span> </li> <li class="rSidebar__options-li" data-filter=".case-study"> <span class="rSidebar__options-square"></span> <span class="rSidebar__options-label d-block buttonTemp" data-filter=".case-study">Case Studies</span> </li> </ul> </div>
2. 调整JavaScript筛选逻辑
修改筛选存储结构和筛选器生成逻辑,支持同一组内多选,同时生成符合Isotope语法的筛选字符串:
document.addEventListener('DOMContentLoaded', function() { var container = document.querySelector('.grid'); const optionLinks = document.querySelectorAll('.rSidebar__options-li'); var iso = new Isotope(container, { itemSelector: '.resourceCard', layoutMode: 'fitRows', transitionDuration: '0.5s', }); // 存储每个筛选组的选中值,格式:{ tag: ['.pdf'], type: ['.blogs-and-news', '.case-study'] } var filters = {}; // 生成符合Isotope语法的筛选器字符串 function generateFilterString(filters) { // 遍历每个筛选组,将组内选中值用逗号拼接(或关系) const groupFilters = Object.values(filters).filter(group => group.length > 0) .map(group => group.join(',')); // 不同组的筛选器用空格拼接(且关系),如果没有选中项则返回'*'(显示全部) return groupFilters.length > 0 ? groupFilters.join(' ') : '*'; } function handleFilterClick(event) { var listItem = event.currentTarget; // 使用currentTarget避免事件冒泡问题 var filterGroup = listItem.closest('.rSidebar__options').getAttribute('data-filter-group'); var filterValue = listItem.getAttribute('data-filter'); // 初始化该组的选中数组 if (!filters[filterGroup]) { filters[filterGroup] = []; } // 切换选中状态:存在则移除,不存在则添加 const valueIndex = filters[filterGroup].indexOf(filterValue); if (valueIndex > -1) { filters[filterGroup].splice(valueIndex, 1); } else { filters[filterGroup].push(filterValue); } // 生成筛选器并执行布局 const filterString = generateFilterString(filters); iso.arrange({ filter: filterString }); } optionLinks.forEach(function(optionLink) { optionLink.addEventListener('click', function(event) { event.preventDefault(); this.classList.toggle('selected'); handleFilterClick(event); }); }); });
3. 关键逻辑说明
- 筛选存储结构:用对象存储每个筛选组的选中值数组,比如
{ tag: ['.pdf'], type: ['.blogs-and-news', '.case-study'] } - 筛选器生成规则:
- 同一筛选组内的多个选项用
逗号拼接,表示“或”关系(匹配组内任意一个选项) - 不同筛选组的筛选器用
空格拼接,表示“且”关系(同时匹配所有组的筛选条件) - 若没有任何选中项,返回
*表示显示全部内容
- 同一筛选组内的多个选项用
- 事件处理优化:使用
event.currentTarget避免事件冒泡导致的目标元素错误(比如点击到内部的span标签时仍能正确获取选中的li元素)
完整代码(含CSS)
.post { padding: 100px; } .rSidebar__box { margin-bottom: 30px; } .rSidebar__options { padding-left: 0; } .rSidebar__options-li { margin-bottom: 17px; display: flex; align-items: center; cursor: pointer; width: fit-content; } .rSidebar__options-li.selected .rSidebar__options-square { background-color: #185A7D; } .rSidebar__options-square { height: 20px; width: 20px; transition: all 0.5s ease; border: 2px solid #000000; } .rSidebar__options-label { margin-left: 10px; } .grid { display: flex; flex-wrap: wrap; margin: -14px 0 0 -14px; } .grid-item { box-sizing: border-box; width: calc(33.33% - 14px); margin: 14px 0 18px 14px; }
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <script src="https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js"></script> <div class="post"> <div class="container"> <div class="row justify-content-between"> <!-- SIDEBAR --> <div class="col-3"> <div class="rSidebar"> <!-- tags --> <div class="rSidebar__box"> <span class="rSidebar__label d-block fw-bold">Filter by tag</span> <ul class="rSidebar__options button-group" data-filter-group="tag"> <li class="rSidebar__options-li" data-filter=".pdf"> <span class="rSidebar__options-square"></span> <span class="rSidebar__options-label d-block buttonTemp" data-filter=".pdf">PDF</span> </li> <li class="rSidebar__options-li" data-filter=".article"> <span class="rSidebar__options-square"></span> <span class="rSidebar__options-label d-block buttonTemp" data-filter=".article">Article</span> </li> </ul> </div> <!-- type --> <div class="rSidebar__box"> <span class="rSidebar__label d-block fw-bold">Filter by type</span> <ul class="rSidebar__options button-group" data-filter-group="type"> <li class="rSidebar__options-li" data-filter=".blogs-and-news"> <span class="rSidebar__options-square"></span> <span class="rSidebar__options-label d-block buttonTemp" data-filter=".blogs-and-news">Blog & News</span> </li> <li class="rSidebar__options-li" data-filter=".case-study"> <span class="rSidebar__options-square"></span> <span class="rSidebar__options-label d-block buttonTemp" data-filter=".case-study">Case Studies</span> </li> </ul> </div> </div> </div> <!-- END --> <!-- GRID --> <div class="col-7"> <div class="grid"> <article class="resourceCard grid-item case-study pdf"><span class="resourceCard__body-title">Case study, PDF post</span></article> <article class="resourceCard grid-item blogs-and-news"><span class="resourceCard__body-title">Blogs and news post</span></article> <article class="resourceCard grid-item blogs-and-news article"><span class="resourceCard__body-title">Blogs and news, article post</span></article> </div> </div> <!-- END --> </div> </div> </div>
内容的提问来源于stack exchange,提问作者Freddy
相关产品推荐
相关产品推荐

