Isotope JS filter not working when search field is used in conjunction with buttons

37 Views Asked by At

I'm using Isotope.js for filtering which includes buttons and a search bar. I know combination filters can work in Isotope because this Codepen showcases a working demo of it.

However, adapting the above to my use case doesn't seem to work.

See my demo here:

document.addEventListener('DOMContentLoaded', function() {

  // store filter for each group
  var buttonFilters = {};
  var buttonFilter;
  // quick search regex
  var qsRegex;

  var container = document.querySelector('.grid');
  var gridItems = container.querySelectorAll('.grid-item');
  var searchInput = document.querySelector('.rSidebar__search');
  var resourceCards = document.querySelectorAll(".resourceCard");

  /*
  * init Isotope
  */

  var iso = new Isotope(container, {
    itemSelector: '.resourceCard',
    layoutMode: 'fitRows',
    transitionDuration: '0.5s',
    // filter: function( itemElem ) {
    //   return qsRegex ? itemElem.textContent.match( qsRegex ) : true;
    // },
    filter: function (itemElem) {
      var item = this;
      var $item = item;
      var textContent = $item.textContent;

      // Check if textContent is defined before calling match
      var searchResult = qsRegex ? itemElem.textContent.match( qsRegex ) : true;
      var buttonResult = buttonFilter ? $item.is(buttonFilter) : true;
      return searchResult && buttonResult;
    },
  });

  /*
  * search filtering
  */

  // use value of search field to filter
  searchInput.addEventListener('keyup', debounce(function () {
    qsRegex = new RegExp(searchInput.value, 'gi');
    iso.arrange();
  }, 200));


  // flatten object by concatting values
  function concatValues( obj ) {
    var value = '';
    for ( var prop in obj ) {
      value += obj[ prop ];
    }
    return value;
  }

  // debounce so filtering doesn't happen every millisecond
  function debounce( fn, threshold ) {
    var timeout;
    threshold = threshold || 100;
    return function debounced() {
      clearTimeout( timeout );
      var args = arguments;
      var _this = this;
      function delayed() {
        fn.apply( _this, args );
      }
      timeout = setTimeout( delayed, threshold );
    };
  }

  /*
  * Create functions to sort isotope cards
  */

  function sortCards(){
    var selectedFilters = [];
    document.querySelectorAll('.selected').forEach(function(selectedItem) {
      selectedFilters.push(selectedItem.getAttribute('data-filter'));
    });
    var selector = selectedFilters.join(', ');
    iso.arrange({ filter: selector });
  }

  /*
  * Perform isotope filtering on li click
  */

  const optionLinks = document.querySelectorAll('.rSidebar__options-li');

  optionLinks.forEach(function(optionLink) {
    optionLink.addEventListener('click', function(event) {
      event.preventDefault();

      console.log(event);

      this.classList.toggle('selected');
      sortCards();

      // Scroll to the top of the #all-posts element smoothly
      const allPostsElement = document.getElementById('all-posts');
      if (allPostsElement) {
        allPostsElement.scrollIntoView({ behavior: 'smooth' });
      }
    });
  });

  iso.layout();


  /* another test */

  // var optionLinks2 = document.querySelectorAll('.rSidebar__options-li');
  //
  // // Add a click event listener to the 'filters' element
  // optionLinks2.forEach(function(optionLink2) {
  //   optionLink2.addEventListener('click', function(event) {
  //
  //
  //     var target = event.target;
  //
  //     console.log(target);
  //
  //     // Check if the clicked element has the class 'button'
  //     if (target.classList.contains('buttonTemp')) {
  //       var buttonGroup = target.closest('.button-group');
  //       console.log("buttonGroup " + buttonGroup);
  //       var filterGroup = buttonGroup.getAttribute('data-filter-group');
  //       console.log("filterGroup " + filterGroup);
  //       var dataFilter = target.getAttribute('data-filter');
  //       console.log("dataFilter " + dataFilter);
  //
  //       // Set filter for the group
  //       buttonFilters[filterGroup] = dataFilter;
  //
  //       // Combine filters
  //       var buttonFilter = concatValues(buttonFilters);
  //
  //       // Isotope arrange
  //       iso.layout();
  //
  //
  //
  //     }
  //   });
  // });

  /*
  * end
  */

});
.container {
  padding: 100px 0;
}

.rSidebar__options {
  padding-left: 0;
}
.rSidebar__options-li {
  margin-bottom: 17px;
  display: flex;
  align-items: center;
  cursor: pointer;
  width: -webkit-fit-content;
  width: -moz-fit-content;
  width: fit-content;
}
.rSidebar__options-li.selected .rSidebar__options-square {
  background-color: blue;
}
.rSidebar__options-square {
  height: 20px;
  width: 20px;
  border: 2px solid black;
}
.rSidebar__options-label {
  margin-left: 10px;
}

.resourceCard {
  border: 2px solid black;
}
<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="container">
  <div class="row justify-content-between">

    <div class="col-3">
      <div class="rSidebar">
        <input class="rSidebar__search" type="text" placeholder="Search" name="search" aria-label="Search">
        <ul class="rSidebar__options button-group" data-filter-group="type">
          <li class="rSidebar__options-li" data-filter=".blogs-and-news" data-query="blogs-and-news">
            <span class="rSidebar__options-square"></span>
            <span class="rSidebar__options-label d-block buttonTemp" data-filter=".blogs-and-news">Blog & News</span>
          </li>
            <li class="rSidebar__options-li" data-filter=".case-study" data-query="case-studies">
            <span class="rSidebar__options-square"></span>
            <span class="rSidebar__options-label d-block buttonTemp" data-filter=".case-study">Case Studies</span>
          </li>
        </ul>
      </div>
    </div>

    <div class="col-7">
      <div class="grid">

        <article class="resourceCard grid-item case-study">
          <div class="resourceCard__body background--white d-flex flex-column">
            <span class="resourceCard__body-title fw-bold" role="heading">Case study post</span>
          </div>
        </article>

        <article class="resourceCard grid-item blogs-and-news">
          <div class="resourceCard__body background--white d-flex flex-column">
            <span class="resourceCard__body-title fw-bold" role="heading">Blogs and news post</span>
          </div>
        </article>

      </div>
    </div>

  </div>
</div>

In the above, perform the following steps to recreate the issue(s):

  1. When running the demo, search for "news" and it will filter to the news post (working)
  2. Rerun the demo and now click either of the checkbox options. This will sort the cards accordingly (working).
  3. Rerun the demo and now, enable both checkboxes, and then search for "news". This will not filter the cards (not working), to show the news post.
  4. Similar to the above, when a checkbox is enabled, and you search for a term that isn't present (e.g. "test"), it doesn't hide all the cards (to imply that no results found with that searched term).

I'm stumped with this one as I've tried to follow the demo and alter my code, but cannot see where my issue lies?

0

There are 0 best solutions below