Home > Blockchain >  In a JavaScript filter, why are placeholder spaces created after filtering?
In a JavaScript filter, why are placeholder spaces created after filtering?

Time:12-21

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>

  • Related