I have two content type areas which contain unique filter options. These are:
typetag
I'm trying to utilise isotope.js, to achieve a dual filtering layout, but it always gives the last clicked filter priority.
The way I want it to function is:
- If only one
tagis selected, then show results with thattag - If more than one
tagis selected, then show results that have any of those tags (both do not need to exist together on a card) - If a
typeis selected, only show results that fall under thattype - If one
typeis selected and onetag, show results for posts where thetagexists on the selectedtype - If one
typeis selected and multipletags, then show posts that fall under that type that have either of thosetags. - If more than one
typeis selected and one or moretags, then show posts where thetags exist on either type
For example, using my demo below, here are some use cases:
- If I click on "Video Demos & Tours", I should see two video posts (card 1 and 3) - WORKS
- If I click on "Video Demos & Tours" and then "Ansible", I should see only see card 3 - DOESN'T WORK
- If I click on "Blog & News", I should see 3 cards (card 2, 4, 5) - WORKS
- If I click on "Blog & News" and then "Ansible", I should see cards 4 and 5
- If I click on "Blog & News", "Ansible" and then "Automation", I should see cards 2,4 and 5
However, in my current demo, though the console log seems to be on the right lines, it doesn't perform the way I intent it to.
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>
If I understand correctly, you want to combine filters of the same group with OR and between groups with AND:
In selectors (as used by Isotope), that would be:
You can build this by going over each group, combining every element with the previously build selectors:
If you prefer regular loops, that is something like:
Some general hints and recommendations:
automationclass, not sure if that is intentionalfilterobject with empty arrays for all existing groups, it spares you figuring out if the group property already exists later onHave a look at the updated snippet:
Does that make sense, does it help?