I am trying to create a filter system that uses data-attributes, I can get it to work if the items selected match the order of the elements data-attributes eg: items selected {fun, easy, cheap} and the elements attributes are in the same order but if I click {easy, cheap, fun} then I don't have any results returned. Any help on solving this would be greatly appreciated.
var filterBtns = document.querySelectorAll('.request-btn'),
slider = document.querySelector('.slider'),
cardContainers = $('.cards-container'),
selectedFilters = [];
filterBtns.forEach(function(el) {
el.addEventListener('click', function() {
this.classList.toggle('clicked');
if (this.classList.contains('clicked')) {
selectedFilters.push(this.dataset.filter);
} else {
selectedFilters.splice(selectedFilters.indexOf(this.dataset.filter), 1);
}
updateCards();
});
});
slider.addEventListener('change', function() {
if (this.value === '0') {
selectedFilters.push('easy');
} else if (this.value === '1') {
selectedFilters.splice(selectedFilters.indexOf('easy', 'diy'), 1);
} else {
selectedFilters.push('diy');
}
updateCards();
});
var updateCards = function() {
cardContainers.removeClass('show').filter(function() {
var data = this.dataset;
var selectedFiltersValues = selectedFilters.join(' ');
return selectedFilters.length ? data.filter.includes(selectedFiltersValues) : true;
}).addClass('show');
}
.card {
width: 200px;
border: 1px solid coral;
margin: 15px;
padding: 15px;
float: left;
}
.cards-container {
display: none;
}
.show {
display: block;
}
.request-btn.clicked {
background-color: coral;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="filter-container">
<div class="left-filter">
<p>EASY<input type="range" class="slider provider-complexity" min="0" max="2" value="1" step="1">DIY</p>
</div>
<div class="right-filter">
<button class="request-btn" data-filter="cheap">Cheap</button>
<button class="request-btn" data-filter="fun">fun</button>
<button class="request-btn" data-filter="green">green</button>
<button class="request-btn" data-filter="big">big</button>
</div>
</div>
<div class="cards-container show" data-filter="green">
<div class="card">1 the tag is - green</div>
</div>
<div class="cards-container show" data-filter="fun">
<div class="card">2 the tag is - fun</div>
</div>
<div class="cards-container show" data-filter="cheap">
<div class="card">3 the tag is - cheap</div>
</div>
<div class="cards-container show" data-filter="big">
<div class="card">4 the tag is - big</div>
</div>
<div class="cards-container show" data-filter="cheap big">
<div class="card">5 the tags are - cheap big</div>
</div>
<div class="cards-container show" data-filter="fun easy cheap">
<div class="card">6 the tags are - fun easy cheap</div>
</div>
<div class="cards-container show" data-filter="diy">
<div class="card">7 the tag is - diy</div>
</div>
<div class="cards-container show" data-filter="easy">
<div class="card">8 hthe tag is - easy</div>
</div>
<div class="cards-container show" data-filter="easy green">
<div class="card">9 the tags are - easy green</div>
</div>
Update based on comment
Based on the comment, the requirement is that each item must contain all of the filters. If "cheap" and "big" are selected, only items that have "cheap" and "big" can be returned.
The change:
// Get the individual item filters as an array
var itemData = data.filter.split(' ')
return selectedFilters.length
// match on every
? selectedFilters.every(function (val) {
return itemData.indexOf(val) > -1;
})
: true;
var filterBtns = document.querySelectorAll('.request-btn'),
slider = document.querySelector('.slider'),
cardContainers = $('.cards-container'),
selectedFilters = [];
filterBtns.forEach(function(el) {
el.addEventListener('click', function() {
this.classList.toggle('clicked');
if (this.classList.contains('clicked')) {
selectedFilters.push(this.dataset.filter);
} else {
selectedFilters.splice(selectedFilters.indexOf(this.dataset.filter), 1);
}
updateCards();
});
});
slider.addEventListener('change', function() {
if (this.value === '0') {
selectedFilters.push('easy');
} else if (this.value === '1') {
selectedFilters.splice(selectedFilters.indexOf('easy', 'diy'), 1);
} else {
selectedFilters.push('diy');
}
updateCards();
});
var updateCards = function() {
cardContainers.removeClass('show').filter(function() {
var itemData = data.filter.split(' ')
return selectedFilters.length
? selectedFilters.every(function (val) {
return itemData.indexOf(val) > -1;
})
: true;
}).addClass('show');
}
.card {
width: 200px;
border: 1px solid coral;
margin: 15px;
padding: 15px;
float: left;
}
.cards-container {
display: none;
}
.show {
display: block;
}
.request-btn.clicked {
background-color: coral;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="filter-container">
<div class="left-filter">
<p>EASY<input type="range" class="slider provider-complexity" min="0" max="2" value="1" step="1">DIY</p>
</div>
<div class="right-filter">
<button class="request-btn" data-filter="cheap">Cheap</button>
<button class="request-btn" data-filter="fun">fun</button>
<button class="request-btn" data-filter="green">green</button>
<button class="request-btn" data-filter="big">big</button>
</div>
</div>
<div class="cards-container show" data-filter="green">
<div class="card">1 the tag is - green</div>
</div>
<div class="cards-container show" data-filter="fun">
<div class="card">2 the tag is - fun</div>
</div>
<div class="cards-container show" data-filter="cheap">
<div class="card">3 the tag is - cheap</div>
</div>
<div class="cards-container show" data-filter="big">
<div class="card">4 the tag is - big</div>
</div>
<div class="cards-container show" data-filter="cheap big">
<div class="card">5 the tags are - cheap big</div>
</div>
<div class="cards-container show" data-filter="fun easy cheap">
<div class="card">6 the tags are - fun easy cheap</div>
</div>
<div class="cards-container show" data-filter="diy">
<div class="card">7 the tag is - diy</div>
</div>
<div class="cards-container show" data-filter="easy">
<div class="card">8 hthe tag is - easy</div>
</div>
<div class="cards-container show" data-filter="easy green">
<div class="card">9 the tags are - easy green</div>
</div>
Original
You problem is that you've joined your selected filters, in your updateCards
function you're then checking if the items data-filter
includes cheap fun green
.
The solution is to check if the array of selectedFilters
includes the elements attribute(s).
The change
const itemData = data.filter.split(' ')
return selectedFilters.length
? selectedFilters.every(function (val) {
return itemData.indexOf(val) > -1;
})
: true;
I've updated your example below.
A note on the usage of .some
It works across all major browsers, if you need to support i.e 6-8 for some hellish reason, you will need a polyfill. CanIUse
var filterBtns = document.querySelectorAll('.request-btn'),
slider = document.querySelector('.slider'),
cardContainers = $('.cards-container'),
selectedFilters = [];
filterBtns.forEach(function(el) {
el.addEventListener('click', function() {
this.classList.toggle('clicked');
if (this.classList.contains('clicked')) {
selectedFilters.push(this.dataset.filter);
} else {
selectedFilters.splice(selectedFilters.indexOf(this.dataset.filter), 1);
}
updateCards();
});
});
slider.addEventListener('change', function() {
if (this.value === '0') {
selectedFilters.push('easy');
} else if (this.value === '1') {
selectedFilters.splice(selectedFilters.indexOf('easy', 'diy'), 1);
} else {
selectedFilters.push('diy');
}
updateCards();
});
var updateCards = function() {
cardContainers.removeClass('show').filter(function() {
var data = this.dataset;
return selectedFilters.length
? selectedFilters.some(function (val) {
return data.filter.includes(val);
})
: true;
}).addClass('show');
}
.card {
width: 200px;
border: 1px solid coral;
margin: 15px;
padding: 15px;
float: left;
}
.cards-container {
display: none;
}
.show {
display: block;
}
.request-btn.clicked {
background-color: coral;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="filter-container">
<div class="left-filter">
<p>EASY<input type="range" class="slider provider-complexity" min="0" max="2" value="1" step="1">DIY</p>
</div>
<div class="right-filter">
<button class="request-btn" data-filter="cheap">Cheap</button>
<button class="request-btn" data-filter="fun">fun</button>
<button class="request-btn" data-filter="green">green</button>
<button class="request-btn" data-filter="big">big</button>
</div>
</div>
<div class="cards-container show" data-filter="green">
<div class="card">1 the tag is - green</div>
</div>
<div class="cards-container show" data-filter="fun">
<div class="card">2 the tag is - fun</div>
</div>
<div class="cards-container show" data-filter="cheap">
<div class="card">3 the tag is - cheap</div>
</div>
<div class="cards-container show" data-filter="big">
<div class="card">4 the tag is - big</div>
</div>
<div class="cards-container show" data-filter="cheap big">
<div class="card">5 the tags are - cheap big</div>
</div>
<div class="cards-container show" data-filter="fun easy cheap">
<div class="card">6 the tags are - fun easy cheap</div>
</div>
<div class="cards-container show" data-filter="diy">
<div class="card">7 the tag is - diy</div>
</div>
<div class="cards-container show" data-filter="easy">
<div class="card">8 hthe tag is - easy</div>
</div>
<div class="cards-container show" data-filter="easy green">
<div class="card">9 the tags are - easy green</div>
</div>