Issue
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 class="card ${(details.inProgress == `blocked`) ? `blocked` : (details.inProgress == `working`) ? `working` : `complete`}" data-filter="${details.inProgress}">
      <h5 class="card__title">${details.title}</h5>
      <div class="card__body">
        <div class="card__icon">
          <i class="${details.icon} fa-3x"></i>
        </div>
      </div>
      <div class="card__footer">
        <div class="task">
          <a href="${details.task}" target="_blank">Task</a></div>
        <div class="component">
          <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 class="filters">
      <span class="filter" data-filter="all">All</span>
      <span class="filter" data-filter="blocked">Blocked</span>
      <span class="filter" data-filter="working">Working</span>
      <span class="filter" data-filter="complete">Complete</span>
    </div>
    <div class="cards" id="hubCardsDisplay"></div>
  </main>Solution
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 class="card ${(details.inProgress == `blocked`) ? `blocked` : (details.inProgress == `working`) ? `working` : `complete`}" data-filter="${details.inProgress}">
      <h5 class="card__title">${details.title}</h5>
      <div class="card__body">
        <div class="card__icon">
          <i class="${details.icon} fa-3x"></i>
        </div>
      </div>
      <div class="card__footer">
        <div class="task">
          <a href="${details.task}" target="_blank">Task</a></div>
        <div class="component">
          <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 class="filters">
    <span class="filter" data-filter="all">All</span>
    <span class="filter" data-filter="blocked">Blocked</span>
    <span class="filter" data-filter="working">Working</span>
    <span class="filter" data-filter="complete">Complete</span>
  </div>
  <div class="cards" id="hubCardsDisplay"></div>
</main>Answered By - Kinglish
 
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.