In the snippet below, I'm trying to use a click filter on a set of cards. When I click any of the filters, the elements are filtered properly, but there are space holders where other cards once were.
How do I filter these so there are no large empty spaces where cards were filtered out?
const hubCards = [
{
title: 'Lists',
icon: 'fas fa-list-ul',
task: '#',
component: '#',
inProgress: 'working'
},
{
title: 'Checkboxes',
icon: 'fas fa-check-square',
task: '#',
component: '#',
inProgress: 'working'
},
{
title: 'Footer',
icon: 'fas fa-sort-amount-down-alt',
task: '#',
component: '#',
inProgress: 'complete'
},
{
title: 'Text Fields',
icon: 'fas fa-align-left',
task: '#',
component: '#',
inProgress: 'working'
},
{
title: 'Buttons',
icon: 'fas fa-mouse',
task: '#',
component: '#',
inProgress: 'complete'
},
{
title: 'Navigation',
icon: 'fas fa-bars',
task: '#',
component: '#',
inProgress: 'blocked'
}
];
hubCards.sort((a, b) => a.title.localeCompare(b.title));
let hubCardsTemplate = (details) => {
return `
<div data-filter="${details.inProgress}">
<h5 >${details.title}</h5>
<div >
<div >
<i ></i>
</div>
</div>
<div >
<div >
<a href="${details.task}" target="_blank">Task</a></div>
<div >
<a href="${details.component}">Component</a>
</div>
</div>
</div>
`
}
//output
document.getElementById('hubCardsDisplay').innerHTML = `
${hubCards.map(hubCardsTemplate).join(' ')}
`;
//filter
const filters = document.querySelectorAll('.filter');
filters.forEach(filter => {
filter.addEventListener('click', function() {
let selectedFilter = filter.getAttribute('data-filter');
let itemsToHide = document.querySelectorAll(`.cards .card:not([data-filter='${selectedFilter}'])`);
let itemsToShow = document.querySelectorAll(`.cards [data-filter='${selectedFilter}']`);
if (selectedFilter == 'all') {
itemsToHide = [];
itemsToShow = document.querySelectorAll('.cards [data-filter]');
}
itemsToHide.forEach(el => {
el.classList.add('hide');
el.classList.remove('show');
});
itemsToShow.forEach(el => {
el.classList.remove('hide');
el.classList.add('show');
});
});
});
ul.filters {
display: flex;
justify-content: space-between;
padding: 0;
}
ul.filters li {
list-style-type: none;
cursor: pointer;
padding: 2px 5px;
}
ul.filters li.active {
background: #8ccf8e;
color: #fff;
}
.hide {
animation: hide 0.5s ease 0s 1 normal forwards;
transform-origin: center;
}
.show {
animation: show 0.5s ease 0s 1 normal forwards;
transform-origin: center;
}
@keyframes hide {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
width: 0;
height: 0;
margin: 0;
}
}
@keyframes show {
0% {
transform: scale(0);
width: 0;
height: 0;
margin: 0;
}
100% {
transform: scale(1);
}
}
.cards {
display: flex;
flex-direction: column;
}
@media (min-width: 576px) {
.cards {
flex-wrap: wrap;
flex-direction: row;
}
}
.cards .card {
border: 3px solid #e12d2d;
display: flex;
flex-direction: column;
}
.cards .card.blocked {
background: linear-gradient(
to right,
rgba(225, 45, 45, 0.5),
rgba(225, 45, 45, 0.5)
);
background-size: cover;
border-color: #e12d2d;
}
.cards .card.working {
background: linear-gradient(
to right,
rgba(255, 184, 0, 0.5),
rgba(255, 184, 0, 0.5)
);
border-color: #ffb800;
}
.cards .card.complete {
background: linear-gradient(
to right,
rgba(52, 131, 55, 0.5),
rgba(52, 131, 55, 0.5)
);
border-color: #348337;
}
.cards .card__title {
background: #b7bec8;
width: 100%;
margin-top: auto;
text-align: center;
padding: 0.5rem 0;
text-transform: uppercase;
}
.cards .card:not(:first-child),
.cards .card:not(:last-child) {
margin: 0.2rem 0;
}
@media (min-width: 576px) {
.cards .card:not(:first-child),
.cards .card:not(:last-child) {
margin-left: 0.2rem;
margin-right: 0.2rem;
}
}
@media (min-width: 576px) {
.cards .card {
flex: 48%;
}
}
@media (min-width: 768px) {
.cards .card {
flex: 23%;
}
}
.cards .card__body {
display: flex;
justify-content: center;
align-items: center;
}
.cards .card__footer {
margin: 1rem 0;
background: #191e24;
display: flex;
justify-content: space-around;
margin-bottom: auto;
}
.cards .card__footer .task,
.cards .card__footer .component {
display: flex;
justify-content: center;
align-items: center;
color: #fff;
flex: 50%;
padding: 0.5rem 0;
cursor: pointer;
transition: all 0.2s ease;
}
.cards .card__footer .task:hover,
.cards .card__footer .component:hover {
background: #b7bec8;
}
.cards .card__footer .task:hover a,
.cards .card__footer .component:hover a {
color: #fff;
font-weight: 300;
}
.cards .card__footer .task a,
.cards .card__footer .component a {
color: #fff;
font-weight: 300;
}
.cards .card__footer .task a:hover,
.cards .card__footer .component a:hover {
color: #191e24;
}
.cards .card__footer .task::before,
.cards .card__footer .component::before {
font-family: "Font Awesome 5 Free";
content: "";
margin-right: 0.3rem;
}
.cards .card__footer button {
border: 0;
background: #191e24;
color: #fff;
padding: 0.5rem 1rem;
}
<main>
<div >
<span data-filter="all">All</span>
<span data-filter="blocked">Blocked</span>
<span data-filter="working">Working</span>
<span data-filter="complete">Complete</span>
</div>
<div id="hubCardsDisplay"></div>
</main>
CodePudding user response:
This isn't exactly the same effect, but if you make the hidden cells position:absolute
it will take care of the space issue.
const hubCards = [{
title: 'Lists',
icon: 'fas fa-list-ul',
task: '#',
component: '#',
inProgress: 'working'
},
{
title: 'Checkboxes',
icon: 'fas fa-check-square',
task: '#',
component: '#',
inProgress: 'working'
},
{
title: 'Footer',
icon: 'fas fa-sort-amount-down-alt',
task: '#',
component: '#',
inProgress: 'complete'
},
{
title: 'Text Fields',
icon: 'fas fa-align-left',
task: '#',
component: '#',
inProgress: 'working'
},
{
title: 'Buttons',
icon: 'fas fa-mouse',
task: '#',
component: '#',
inProgress: 'complete'
},
{
title: 'Navigation',
icon: 'fas fa-bars',
task: '#',
component: '#',
inProgress: 'blocked'
}
];
hubCards.sort((a, b) => a.title.localeCompare(b.title));
let hubCardsTemplate = (details) => {
return `
<div data-filter="${details.inProgress}">
<h5 >${details.title}</h5>
<div >
<div >
<i ></i>
</div>
</div>
<div >
<div >
<a href="${details.task}" target="_blank">Task</a></div>
<div >
<a href="${details.component}">Component</a>
</div>
</div>
</div>
`
}
//output
document.getElementById('hubCardsDisplay').innerHTML = `
${hubCards.map(hubCardsTemplate).join(' ')}
`;
//filter
const filters = document.querySelectorAll('.filter');
filters.forEach(filter => {
filter.addEventListener('click', function() {
let selectedFilter = filter.getAttribute('data-filter');
let itemsToHide = document.querySelectorAll(`.cards .card:not([data-filter='${selectedFilter}'])`);
let itemsToShow = document.querySelectorAll(`.cards [data-filter='${selectedFilter}']`);
if (selectedFilter == 'all') {
itemsToHide = [];
itemsToShow = document.querySelectorAll('.cards [data-filter]');
}
itemsToHide.forEach(el => {
el.classList.add('hide');
el.classList.remove('show');
});
itemsToShow.forEach(el => {
el.classList.remove('hide');
el.classList.add('show');
});
});
});
ul.filters {
display: flex;
justify-content: space-between;
padding: 0;
}
ul.filters li {
list-style-type: none;
cursor: pointer;
padding: 2px 5px;
}
ul.filters li.active {
background: #8ccf8e;
color: #fff;
}
.hide {
animation: hide 0.5s ease 0s 1 normal forwards;
transform-origin: center;
}
.show {
animation: show 0.5s ease 0s 1 normal forwards;
transform-origin: center;
}
@keyframes hide {
0% {
transform: scale(1);
position: absolute;
}
100% {
position: absolute;
transform: scale(0);
}
}
@keyframes show {
0% {
position: relative;
transform: scale(0);
}
100% {
transform: scale(1);
}
}
.cards {
display: flex;
flex-direction: column;
}
@media (min-width: 576px) {
.cards {
flex-wrap: wrap;
flex-direction: row;
}
}
.cards .card {
border: 3px solid #e12d2d;
display: flex;
flex-direction: column;
}
.cards .card.blocked {
background: linear-gradient( to right, rgba(225, 45, 45, 0.5), rgba(225, 45, 45, 0.5));
background-size: cover;
border-color: #e12d2d;
}
.cards .card.working {
background: linear-gradient( to right, rgba(255, 184, 0, 0.5), rgba(255, 184, 0, 0.5));
border-color: #ffb800;
}
.cards .card.complete {
background: linear-gradient( to right, rgba(52, 131, 55, 0.5), rgba(52, 131, 55, 0.5));
border-color: #348337;
}
.cards .card__title {
background: #b7bec8;
width: 100%;
margin-top: auto;
text-align: center;
padding: 0.5rem 0;
text-transform: uppercase;
}
.cards .card:not(:first-child),
.cards .card:not(:last-child) {
margin: 0.2rem 0;
}
@media (min-width: 576px) {
.cards .card:not(:first-child),
.cards .card:not(:last-child) {
margin-left: 0.2rem;
margin-right: 0.2rem;
}
}
@media (min-width: 576px) {
.cards .card {
flex: 48%;
}
}
@media (min-width: 768px) {
.cards .card {
flex: 23%;
}
}
.cards .card__body {
display: flex;
justify-content: center;
align-items: center;
}
.cards .card__footer {
margin: 1rem 0;
background: #191e24;
display: flex;
justify-content: space-around;
margin-bottom: auto;
}
.cards .card__footer .task,
.cards .card__footer .component {
display: flex;
justify-content: center;
align-items: center;
color: #fff;
flex: 50%;
padding: 0.5rem 0;
cursor: pointer;
transition: all 0.2s ease;
}
.cards .card__footer .task:hover,
.cards .card__footer .component:hover {
background: #b7bec8;
}
.cards .card__footer .task:hover a,
.cards .card__footer .component:hover a {
color: #fff;
font-weight: 300;
}
.cards .card__footer .task a,
.cards .card__footer .component a {
color: #fff;
font-weight: 300;
}
.cards .card__footer .task a:hover,
.cards .card__footer .component a:hover {
color: #191e24;
}
.cards .card__footer .task::before,
.cards .card__footer .component::before {
font-family: "Font Awesome 5 Free";
content: "";
margin-right: 0.3rem;
}
.cards .card__footer button {
border: 0;
background: #191e24;
color: #fff;
padding: 0.5rem 1rem;
}
<main>
<div >
<span data-filter="all">All</span>
<span data-filter="blocked">Blocked</span>
<span data-filter="working">Working</span>
<span data-filter="complete">Complete</span>
</div>
<div id="hubCardsDisplay"></div>
</main>