我有两个内容类型区域,其中包含独特的过滤器选项。这些是:
type
tag
我正在尝试利用 isotope.js 来实现双重过滤布局,但它总是给予最后单击的过滤器优先级。
我希望它发挥作用的方式是:
tag
,则显示带有该 tag
tag
,则显示具有任何这些标签的结果(两者不需要同时存在于一张卡片上)type
,则仅显示属于该 type
type
和一个 tag
,则显示所选 tag
上存在
type
type
并选择了多个 tags
,则显示属于该类型且具有其中任意一个 tags
的帖子。type
和一个或多个 tag
,则显示任一类型中存在 tag
的帖子例如,使用下面的演示,以下是一些用例:
但是,在我当前的演示中,虽然控制台日志似乎位于正确的行,但它并没有按照我的意图执行。
document.addEventListener("DOMContentLoaded", function () {
var container = document.querySelector(".grid");
var gridItems = container.querySelectorAll(".grid-item");
const optionLinks = document.querySelectorAll(".rSidebar__options-li");
var iso = new Isotope(container, {
itemSelector: ".resourceCard",
layoutMode: "fitRows",
transitionDuration: "0.5s"
});
var filters = {};
function concatValues(obj) {
var value = [];
for (var prop in obj) {
value.push(obj[prop]);
}
return value.flat().join(", ");
}
function handleFilterClick(event, filters) {
var listItem = event;
var filterGroup = listItem
.closest(".rSidebar__options")
.getAttribute("data-filter-group");
var data_filter = listItem.getAttribute("data-filter");
if (filterGroup === "type") {
filters[filterGroup] = [data_filter];
} else {
if (!filters[filterGroup]) {
filters[filterGroup] = [];
}
if (listItem.classList.contains("selected")) {
filters[filterGroup].push(data_filter);
} else {
filters[filterGroup] = filters[filterGroup].filter(
(tag) => tag !== data_filter
);
}
}
// Combine the type filter with the selected tag filters using an AND relationship
var filterValues = [];
// Handle type filter
if (filters["type"]) {
filterValues.push("." + filters["type"][0]);
}
// Handle tags filter if it's defined
if (filters["tag"]) {
var selectedType = filters["type"][0];
filters["tag"].forEach((tag) => {
filterValues.push("." + selectedType + "." + tag);
});
}
var finalFilter = filterValues.join(", ");
console.log(finalFilter);
iso.arrange({
filter: finalFilter
});
}
optionLinks.forEach(function (optionLink) {
optionLink.addEventListener("click", function (event) {
event.preventDefault();
this.classList.toggle("selected");
handleFilterClick(this, filters);
});
});
});
.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(45% - 14px);
margin: 14px 0 18px 14px;
border: 2px solid black;
padding: 20px;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/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="ansible">
<span class="rSidebar__options-square"></span>
<span class="rSidebar__options-label d-block" data-filter="ansible">Ansible</span>
</li>
<li class="rSidebar__options-li" data-filter="automation">
<span class="rSidebar__options-square"></span>
<span class="rSidebar__options-label d-block" data-filter="automation">Automation</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" data-filter="blogs-and-news">Blog & News</span>
</li>
<li class="rSidebar__options-li" data-filter="video-demos-tour">
<span class="rSidebar__options-square"></span>
<span class="rSidebar__options-label d-block" data-filter="video-demos-tours">Video Demos & Tours</span>
</li>
</ul>
</div>
<!-- end -->
</div>
</div>
<!-- END -->
<!-- GRID -->
<div class="col-7">
<div class="grid">
<article class="resourceCard grid-item video-demos-tour automation"><span class="resourceCard__body-title">Card 1<br>Type: Video Demo & Tour<br>Tag: Automation</span></article>
<article class="resourceCard grid-item blogs-and-news"><span class="resourceCard__body-title">Card 2<br>Type: Blog & News<br>Tag: Automation</span></article>
<article class="resourceCard grid-item video-demos-tour automation ansible"><span class="resourceCard__body-title">Card 3<br>Type: Video Demo & Tour<br>Tags: Automation, Ansible</span></article>
<article class="resourceCard grid-item blogs-and-news ansible"><span class="resourceCard__body-title">Card 4<br>Type: Blog & News<br>Tag: Ansible</span></article>
<article class="resourceCard grid-item blogs-and-news ansible"><span class="resourceCard__body-title">Card 5<br>Type: Blog & News<br>Tags: Ansible, Automations</span></article>
</div>
</div>
<!-- END -->
</div>
</div>
</div>
如果我理解正确的话,你想用 OR 组合同一组的过滤器,用 AND 组合组之间的过滤器:
{a,b} x {1,2} -> a1|a2|b1|b2
在选择器中(同位素使用的),这将是:
.a.1, .a.2, .b.1, .b.2
您可以通过遍历每个组,将每个元素与之前的构建选择器组合来构建它:
function buildFilterValues(){
return Object.values(filters) // extract filter arrays
.reduce(
(selectors, groupValues) =>
groupValues.length === 0 ? selectors : // skip empty groups
groupValues.flatMap(v => selectors.map(s => `${s}.${v}`)) // combine group elements with existing selectors into new list
, [''])
.join(', ')
}
一些一般提示和建议:
automation
课filter
对象,这样您就无需稍后再弄清楚组属性是否已存在看看更新后的片段:
class Filter{
groups = {
tag: [],
type: [],
}
updateGroup(group, value, isSelected) {
const isActiveValue = this.groups[group].includes(value)
if (isSelected && !isActiveValue){
this.groups[group].push(value)
}
if (!isSelected && isActiveValue){
this.groups[group] = this.groups[group].filter(v => v !== value)
}
}
buildSelector(){
return Object.values(this.groups).reduce(
(selectors, groupValues) =>
groupValues.length === 0 ? selectors :
groupValues.flatMap(v => selectors.map(s => `${s}.${v}`))
,[''] )
.join(', ')
}
updateSelector(group, value, isSelected){
this.updateGroup(group, value, isSelected)
return this.buildSelector()
}
}
document.addEventListener("DOMContentLoaded", function () {
const iso = new Isotope('.grid', {
itemSelector: ".resourceCard",
layoutMode: "fitRows",
transitionDuration: "0.5s"
});
const activeFilter = new Filter()
function onOptionLinkClick(event){
event.preventDefault();
this.classList.toggle("selected");
const isSelected = this.classList.contains("selected")
const group = this
.closest(".rSidebar__options")
.getAttribute("data-filter-group");
const value = this.getAttribute("data-filter");
const filter = activeFilter.updateSelector(group, value, isSelected)
iso.arrange({filter})
console.clear()
console.log('Current filter:', filter)
}
const optionLinks = document.querySelectorAll(".rSidebar__options-li");
optionLinks.forEach(optionLink => optionLink.addEventListener("click", onOptionLinkClick));
})
.post {
padding: 10px;
}
.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(45% - 14px);
margin: 14px 0 18px 14px;
border: 2px solid black;
padding: 20px;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/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="ansible">
<span class="rSidebar__options-square"></span>
<span class="rSidebar__options-label d-block" data-filter="ansible">Ansible</span>
</li>
<li class="rSidebar__options-li" data-filter="automation">
<span class="rSidebar__options-square"></span>
<span class="rSidebar__options-label d-block" data-filter="automation">Automation</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" data-filter="blogs-and-news">Blog & News</span>
</li>
<li class="rSidebar__options-li" data-filter="video-demos-tour">
<span class="rSidebar__options-square"></span>
<span class="rSidebar__options-label d-block" data-filter="video-demos-tours">Video Demos & Tours</span>
</li>
</ul>
</div>
<!-- end -->
</div>
</div>
<!-- END -->
<!-- GRID -->
<div class="col-7">
<div class="grid">
<article class="resourceCard grid-item video-demos-tour automation"><span class="resourceCard__body-title">Card 1<br>Type: Video Demo & Tour<br>Tag: Automation</span></article>
<article class="resourceCard grid-item blogs-and-news automation"><span class="resourceCard__body-title">Card 2<br>Type: Blog & News<br>Tag: Automation</span></article>
<article class="resourceCard grid-item video-demos-tour automation ansible"><span class="resourceCard__body-title">Card 3<br>Type: Video Demo & Tour<br>Tags: Automation, Ansible</span></article>
<article class="resourceCard grid-item blogs-and-news ansible"><span class="resourceCard__body-title">Card 4<br>Type: Blog & News<br>Tag: Ansible</span></article>
<article class="resourceCard grid-item blogs-and-news ansible"><span class="resourceCard__body-title">Card 5<br>Type: Blog & News<br>Tags: Ansible, Automations</span></article>
</div>
</div>
<!-- END -->
</div>
</div>
</div>
这有意义吗?对你有用吗?