Изотопный фильтр, сортировка, поиск и хэш URL - PullRequest
0 голосов
/ 25 октября 2019

Я использую Изотоп, чтобы сортировать, фильтровать и искать страницу продуктов. Все идет нормально. Я застрял в нескольких областях. Я не смог найти в Интернете пример с точной комбинацией функций, которые мне нужны, поэтому мне нужна помощь.

В двух словах, у меня есть несколько выборочных выпадающих меню, которые фильтруют продукты, выбирают порядок сортировки цен и быстрый поискполе. Все работают, но у меня есть некоторые необходимые компенсации.

TO DO:

Поиск не работает после фильтрации. Мне нужно, чтобы он работал с фильтрами.

Добавление сортировки по 'Sale' и 'New In' в раскрывающемся списке выбора цены.

Добавление прослушивателя хеша URL для создания ссылок для фильтрации, т. Е. ссылка на New In отсортирована первой.

веб-сайт изотопов

Выбрать пример

Пример хэша URL

Пример быстрого поиска

Мой текущий JS для фильтрации и сортировки:

        $(document).ready(function(){

// quick search regex
var qsRegex;
var filterValue;
// init Isotope
var $grid = $(".grid").isotope({
  itemSelector: ".grid-item",
  layoutMode: "fitRows",
  getSortData: {
    price: '.t-price parseInt',
    category: '[data-category]',
  },
  filter: function() {
    var $this = $(this);
    var searchResult = qsRegex ? $this.text().match(qsRegex) : true;
    var selectResult = filterValue ? $this.is(filterValue) : true;
    return searchResult  && selectResult;
  }
});

      // bind filter on select change
//$(".filter-select").on("change", function() {
       // get filter value from option value
 // filterValue = $(this).val();
  //console.log(filterValue);
  //$grid.isotope();
//});


// store filter for each group
var filters = {};

$('.filter-select').on( 'change', function( event ) {
  var $select = $( event.target );
  // get group key
  var filterGroup = $select.attr('value-group');
  // set filter for group
  filters[ filterGroup ] = event.target.value;
  // combine filters
  var filterValue = concatValues( filters );
  // set filter for Isotope
  $grid.isotope({ filter: filterValue });
});

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



$('#price-sort').on( 'change', function() {
          var type = $(this).find(':selected').attr('data-sorttype');
          console.log(type);
    var sortValue = this.value;
      if(type=='ass'){$grid.isotope({ sortBy: sortValue , sortAscending: false});}
          else{$grid.isotope({ sortBy: sortValue , sortAscending: true});}
   $grid.isotope({ sortBy: sortValue });
  });


  // change is-checked class on buttons
  $('#price-sort').on( 'change', function() {
    var sortByValue = this.value;
      console.log(sortByValue);
    $grid.isotope({ sortBy: sortByValue});
  });


// use value of search field to filter
var $quicksearch = $(".quicksearch").keyup(
  debounce(function() {
    qsRegex = new RegExp($quicksearch.val(), "gi");
    $grid.isotope();
  })
);
// debounce so filtering doesn't happen every millisecond
function debounce(fn, threshold) {
  var timeout;
  return function debounced() {
    if (timeout) {
      clearTimeout(timeout);
    }
    function delayed() {
      fn();
      timeout = null;
    }
    setTimeout(delayed, threshold || 100);
  };
}


  });

HTML:

    <div id="sort-filter">
        <div id="sort">
                        <select id="price-sort" class="select-css form-control long">
                <option selected disabled class="s-title"> Sort </option>
                <option data-sorttype="dec" value="price">£ Low To High</option>
                <option data-sorttype="ass" value="price">£ High To Low</option>

            </select>

        </div>
        <div class="filters">
                    <select class="filter-select select-css short" value-group="sizes" id="sizes">
                    <option selected disabled class="s-title"> Size </option>
                      <option value="*">All</option>
                      <option value=".XS">XS</option>
                      <option value=".S">S</option>
                      <option value=".M">M</option>
                      <option value=".L">L</option>
                      <option value=".XL">XL</option>
                      <option value=".XXL">XXL</option>
                    </select>
    </div>
    </div>

<div class="container">

    <ul class="grid cs-style-3">
        <div class="grid-sizer"></div>

            <li class="grid-item XS Male Beige Bags Mint">
                <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
                    <figure style="background-image: URL(image.jpg);">
                        <img src="/image2.jpg" alt="hat sale item">
                </figure>
                <div id="pro-deets">
                <h3>hat sale item</h3>
                        <span id="price" class="holderpage">
                            £<span class="price t-price">3</span>

                        </span>
                </div></a>
            </li>

            <li class="grid-item L Female Brown Tops Worn">
                <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
                    <figure style="background-image: URL(image.jpg);">
                        <img src="/image2.jpg" alt="product no sale no new">
                </figure>
                <div id="pro-deets">
                <h3>product no sale no new</h3>
                        <span id="price" class="holderpage">
                            £<span class="price t-price">40</span>

                        </span>
                </div></a>
            </li>

            <li class="grid-item L Female Brown Tops Worn New" data-category="New">
                <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
                    <figure style="background-image: URL(image.jpg);">
                        <img src="/image2.jpg" alt="Skirt">
                </figure>
                <div id="pro-deets">
                <h3>Skirt</h3>
                        <span id="price" class="holderpage">
                            £<span class="price t-price">10</span>

                        </span>
                </div></a>
            </li>

            <li class="grid-item XS Male Beige Bags Mint Sale" data-category="Sale">
                <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
                    <figure style="background-image: URL(image.jpg);">
                        <img src="/image2.jpg" alt="Jacket">
                </figure>
                <div id="pro-deets">
                <h3>Jacket</h3>
                        <span id="price" class="holderpage">
                            £<span class="price sale">30</span>
                            <span class="price">£<span class="t-price">20</span></span>
                        </span>
                </div></a>
            </li>

        </ul>
        </div>  

Ответы [ 2 ]

1 голос
/ 08 ноября 2019

Ответ: часть 2

Для этого ответа это пример 3. Скорее всего, я не смогу предоставить комментарии к этому коду, поскольку этот код достиг ограничения по символам. Несколько комментариев в кодах пришлось убрать. Для пропущенных комментариев предпочтение отдается примеру 1 и примеру 2.

<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://unpkg.com/imagesloaded@4/imagesloaded.pkgd.min.js"></script>
<script src="https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.js"></script>

<script>
"use strict";
$(document).ready(function(){
// Quick Search Regex
var qsRegex;
// Filter Value
var filterValue;
// sortValue & whether to sortAscending
var sortValue;
var sortAscending;
// Grid not initialize yet.
var $grid;
// Last state of all the filters
var lastState = {};

/*
 * Parameter name for quicksearch, filter, and sort
 * Have this here so everything can easily be changed in one place.
 *
 */
var quicksearchParamName = "search";
var filterParamName = "filter";
var sortValueParamName = "sort";
var sortTypeParamName = "sorttype";


/*
 * Regexes for grabbing values from hash parameter.
 *
 */
var quicksearchRegex = RegExp(quicksearchParamName + '=([^&]+)', 'i');
var filterRegex = RegExp(filterParamName + '=([^&]+)' , 'i');
var sortValueRegex = RegExp(sortValueParamName + '=([^&]+)' , 'i');
var sortTypeRegex = RegExp(sortTypeParamName + '=([^&]+)' , 'i');

/*
 * This variable is for the setHash() function to communicate with
 * the filterWithHash() function.
 *
 * There isn't a need to build a hash string, update everything, and then
 * reinterprete that same hash string right after.
 *
 * Thus, there isn't a need to run setHash() and then let filterWithHash()
 * run on hash update.
 */
var gridAlreadyUpdated = false;

/*
 * Assiging filterElements here for easy modification.
 *
 * Currently the hash is something like this "filter=.XS.Jackets.Black"
 * It can always be changed to "filter=size:.XS+type:Jackets+color:black"
 * with some modification to the code.
 */
var $filterElements = $('.filter-select');
/*
 * The preceding $ above mean that $filterElements is a jquery object.
 * The below filterElements is the array of all the elements
 * of $filterElements converted to jquery object.
 *
 * This was done like this so each time a select group is needed it doesn't
 * have to be converted to an object of jquery.
 */
var filterElements = [];
for ( let i = 0; i < $filterElements.length; i++ ){
  filterElements[i] = ($($filterElements[i]));
}

/*
 * Reset filter button, remove if not need.
 *
 */
$("#btn-reset-filter").on('click', function( event ){
  $filterElements.prop('selectedIndex',0);
  // only trigger the event for one element.
  filterElements[0].trigger("change");
});

// use value of search field to filter
var $quicksearch = $("#quicksearch").keyup(
  debounce(function() {
    setHash(1);
  })
);
// debounce so filtering doesn't happen every millisecond
function debounce(fn, threshold) {
  var timeout;
  return function debounced() {
    if (timeout) {
      clearTimeout(timeout);
    }
    function delayed() {
      fn();
      timeout = null;
    }
    setTimeout(delayed, threshold || 100);
  };
}

/*
 * When any of the select field with class filter is change
 * we cycle through all the fields and grab all the values for each field
 * and concat them into the filterValue.
 *
 * Over here filterElements that is being used is the JQuery object.
 */
$filterElements.on( 'change', function( event ) {
  filterValue = "";
  for ( let i = 0; i < $filterElements.length; i++ ){
    if ( $filterElements[i].selectedIndex > 0 ) {
      filterValue += $filterElements[i].value;
    }
  }

  // Use set hash to update the $grid and set the hash.
  setHash(2);
});

/*
 * change is-checked class on buttons
 * Only need one price-sort not two
 *
 */
$('#price-sort').on( 'change', function() {
    setHash(3, this);
});

function removeDisabled() {
  $filterElements.prop('disabled', false);
  $('#quicksearch').prop('disabled', false);
  $('#price-sort').prop('disabled', false);
}


function getHashFilter() {
  // get filter=filterName
  var matches = location.hash.match( filterRegex );
  var hashFilter = matches && matches[1];
  return hashFilter && decodeURIComponent( hashFilter );
}

function getSearchFilter() {
  // get search=filterName
  var matches = location.hash.match( quicksearchRegex );
  var searchFilter = matches && matches[1];
  return searchFilter && decodeURIComponent( searchFilter );
}

/*
 * Get the sort param. This function will always return an array with
 * 2 indexes. If both sortValue and sortType is found then it will return
 * the values for both. Value is index 1, and type is index 2.
 *
 * For everything else, this function will return [null, null].
 */
function getSortParam() {
  var valueMatches = location.hash.match( sortValueRegex );
  var typeMatches = location.hash.match( sortTypeRegex );
  var v = valueMatches && valueMatches[1];
  var t = typeMatches && typeMatches[1];

  if ( v && t ) return [decodeURIComponent( v ), decodeURIComponent( t )];
  return [ null, null ];
}

/*
 * This function will set the hash when one of the filtering field is
 * changed.
 *
 * Parameter whocall is utilize to know who is the caller. There can only
 * be one caller at a time. Whocall is utilize as int because comparing
 * int is much faster than comparing string.
 *
 * whocall(1) = quicksearch
 * whocall(2) = filter
 * whocall(3) = sorting
 *
 * In a secure environment any other whocall besides the 3 above should
 * generate an error.
 */
function setHash ( whocall, obj ){
  var hashes = {};
  var hashStr = "";
  if ( location.hash ){
      /*
       * forEach can be uitlized here, but this provide better cross platform
       * compatibitliy.
       *
       */
    let temp = location.hash.substr(1).split("&");
    for ( let i = 0; i < temp.length; i++ ){
      let param = temp[i].split("=");
      // if param[0] is 0 length that is an invalid look something like &=abc.
      if ( param[0].length === 0 ) continue;
      /*
       * if more than > 2 that is also invalid but just grab the first one anyway.
       * if exactly 1 that is something like &filter=&somethingelse. So that is an
       * empty param.
       *
       */
        let value = param.length > 1? param[1] : '';
        // This does not check if a url receive the same parameter multiple times.
        hashes[param[0]] = value;
      }
    }

  /*
   * If there is a quicksearch value assign that to the hashes object.
   * If not delete quicksearch name from the hashes object if there is.
   * With this way, if there was a value for quicksearch in the hash
   * object, it will just get overwritten. If not that index will be create.
   * The delete statement is just for cosmetic. This we turn the url back
   * to without hashes if there isn't a value.
   * However, for faster code, this can simply be done as
   *
   *   hashes[quicksearchParamName] = $("#quicksearch").val()
   *
   * If do like the above, whether if there is a value or not, the hash
   * parameter for quicksearch will always be built.
   *
   */
  if ( whocall === 1 ){
    // 1 : quicksearch
    if ( $("#quicksearch").val() ) hashes[quicksearchParamName] = encodeURIComponent($("#quicksearch").val());
    else delete hashes[quicksearchParamName];
    qsRegex = new RegExp($("#quicksearch").val(), "gi");
    /*
     * For lastState, if setup correctly, val will give an empty string
     * or something here.
     *
     */
    lastState["searchFilter"] = $("#quicksearch").val();
  } else if ( whocall === 2 ){
    // 2 : filter
    /*
     * If done correctly there will always be a filter value when the user
     * choose an option
     *
     */
    hashes[filterParamName] = encodeURIComponent(filterValue);
    lastState["filterValue"] = filterValue;
  } else {
    // 3 : price sort
    /*
     * If from user selecting, without an option for resetting. If done
     * correctly, there will always be a sortValue and sortType.
     *
     */
    lastState["sortValue"] = sortValue = obj.value;
    lastState["sortType"] = obj.selectedOptions[0].getAttribute('data-sorttype');
    hashes[sortValueParamName] = encodeURIComponent(obj.value);
    hashes[sortTypeParamName] = obj.selectedOptions[0].getAttribute('data-sorttype');
    sortAscending = hashes[sortTypeParamName] === "asc"? true : false;
  }

  /*
   * If the fields are not disabled then update the grid if grid already intialized.
   * Otherwise, the initializer will do that. For that use the code in this comment
   * instead.
   *
   * if ( $grid ) $grid.isotope({ sortBy: sortValue , sortAscending: sortAscending});
   */
  $grid.isotope({ sortBy: sortValue , sortAscending: sortAscending});
  /*
   * Update the hash without making filterWithHash() update the grid.
   * Join everything in hashes together into a string. Always append &
   * after a key. But when assign to "location.hash", remove the last
   * character(extra &) from the string.
   *
   */
  for ( const k in hashes ) hashStr += k + "=" + hashes[k] + "&";
  gridAlreadyUpdated = true;
  location.hash = hashStr.substr(0, hashStr.length - 1);
}

/*
 * This function below can be customize to utilize not just only hashes
 * but also "Get Requests"
 *
 */
function filterWithHash() {
  // If the grid is already updated, there is nothing to do.
  if ( gridAlreadyUpdated ) {
      gridAlreadyUpdated = false;
      return;
  }
  var hashFilter = getHashFilter();
  var searchFilter = getSearchFilter();
  var sortParam = getSortParam();
  /*
   * If the last time we access the value for the filters and it
   * is the same at this time. There isn't a point to re-execute the code
   */
  if ( $grid && lastState["searchFilter"] === searchFilter
             && lastState["filterValue"] === hashFilter
             && lastState["sortValue"] === sortParam[0]
             && lastState["sortType"] === sortParam[1] ) {
      return;
  }

  lastState["sortValue"] = sortParam[0];
  lastState["sortType"] = sortParam[1];
  lastState["searchFilter"] = searchFilter;
  /*
   * Note that the lastState["filterValue"] for here is
   * the string value of hash filter and not the actual
   * processed filterValue.
   *
   */
  lastState["filterValue"] = hashFilter;

  /*
   * If searhFilter is there, utilize it.
   * Else, qsRegex is reset. That is because the user could input a value into the
   * search field and then later delete that value then press enter. If that happen
   * and we don't reset the field, the result will not be reset.
   *
   * The same goes for hashFilter below, it is just easier to use this as an example.
   */
  if ( searchFilter ) {
      $('#quicksearch').val(searchFilter);
      qsRegex = new RegExp(searchFilter, "gi");
  } else {
      // searchhash could be null and that is not fine with RegExp.
      // Hence, we give it an empty string.
      $('#quicksearch').val("");
      qsRegex = new RegExp("", "gi");
  }

  /*
   * Refer to comment of searchFilter right above
   *
   * This will split the hash string by the "." and then check for their values.
   * If they are valid, it will set the filter value by the valid one. It will
   * disregard all the invalid.
   *
   * This search is based on the filterElements. For each filterElements,
   * the code will search for a match in the split array. If that is found,
   * that value is added to the filterValue string.
   *
   * Therefore, any duplicate will not be taken into consideration.
   * Duplicates only can become a problem if values between each filterElements
   * are not unique in nature.
   */
  if ( hashFilter ) {
    hashFilter = hashFilter.split(/(?=\.)/);
    filterValue = "";
    for ( let i = 0; i < filterElements.length; i++ ){
      let foundedValue = false;
      /*
       * This will search through the entire splitted hash array. But can
       * be done to search at maximum the same amount of filterElements
       * that are available.
       *
       * Note that, this does not check or prop the All select field
       * as that field now does not contain any values.
       *
       * Also how many index turned to null can be keep tracked of. That
       * is because if there are 2 indexes and both turned to null but there
       * are 20 filter elements then There is no point executing the code
       * for the other 18 elements.
       *
       * However, for a smaller amount of filter elements, keeping track
       * of how many elements turned to null may create an overhead cost.
       */
      for ( let j = 0; j < hashFilter.length; j++ ){
        if ( hashFilter[j] === null ) continue;
        let selectValue = filterElements[i].find('option[value="'+ hashFilter[j] +'"]');
        /*
         * If selectValue is found then select the value in the element
         * and add that value to the filterValue string.
         *
         * One a value is founded and used, that value will be turned
         * to null and will not be reused.
         *
         * Make sure the value of each select group is unique.
         */
        if ( selectValue.length > 0 ){
          selectValue.prop('selected', 'selected');
          filterValue += hashFilter[j];
          hashFilter[j] = null;
          foundedValue = true;
          break;
        }
      }
      /*
       * If the hash string did not contain a value for this element,
       * prop selected index 0(reset the select field).
       *
       */
      if ( foundedValue === false ) filterElements[i].prop('selectedIndex',0);
    }

    /*
     * If after cycle through the hash above and filterValue's length is still 0
     * then a default value will be prop for everything.
     *
     * Notice the changes here, where $filterElements that is being used
     * is preceding with the $.
     */
    if ( filterValue.length === 0 ) $filterElements.prop('selectedIndex',0);
  } else {
    // filterValue will become null or empty whichever. But that is fine.
    filterValue = hashFilter;
    $filterElements.prop('selectedIndex',0);
  }

  /*
   * getSortParam will always return two index. It just whether if they both have
   * values or both null.
   *
   * If sortParam is [null, null] or its values are invalid. Turn sortValue and
   * sortAscending to null.
   *
   * If sortParam is [null, null] prop the default select for select group
   * with the id price-sort.
   */
  if ( sortParam[0] ){
    // search price sort select group to see if the hash is valid.
    var sortObj = $('#price-sort').find('option[value="'+ sortParam[0] +'"][data-sorttype="'+ sortParam[1] +'"]');
    // If hash is valid prob the field
    // Else reset the field
    if ( sortObj.length > 0 ){
      sortObj.prop('selected', true);
      sortValue = sortParam[0];
      sortAscending = sortParam[1] === "asc"? true : false;
    } else {
      sortValue = null;
      sortAscending = null;
      $('select[id="price-sort"]').prop('selectedIndex', 0);
    }
  } else {
    sortValue = null;
    sortAscending = null;
    $('select[id="price-sort"]').prop('selectedIndex', 0);
  }

  if ( !$grid ) {
    $grid = $(".grid").isotope({
              itemSelector: ".grid-item",
              layoutMode: "fitRows",
              getSortData: {
                price: '.t-price parseInt',
                category: '[data-category]',
                dateAdded: function( itemElem ){
                  // Sort by date: return the timestamp of dateAdded.
                  return new Date($(itemElem).find('.dateAdded').text()).getTime();
                },
                  /*
                   * Sort by item with class New.
                   * If the item contain class New, it will be assigned with a value of 0.
                   * Else, it will be assigned with a value of 1.
                   *
                   * There shouldn't be a sort descending for New. A field like that
                   * wasn't configured. Therefore, hash wouldn't be able to sort
                   * New in a descending way.
                   */
                New: function( itemElem ){
                  return $(itemElem).hasClass('New')? 0 : 1;
                }
              },
              sortBy: sortValue ,
              sortAscending: sortAscending,
              filter: function() {
                /* Note that current filtering using class name. */
                var $this = $(this);
                var searchResult = qsRegex ? $this.text().match(qsRegex) : true;
                var selectResult = filterValue ? $this.is(filterValue) : true;
                return searchResult && selectResult;
              }
            });
  } else $grid.isotope({ sortBy: sortValue , sortAscending: sortAscending});
}

$('.grid').imagesLoaded( function() {
  filterWithHash();
  $(window).on( 'hashchange', filterWithHash );
  removeDisabled();
});

});
</script>
</head>
<body>

<div id="sort-filter">
  <!-- Short Div -->
  <div id="sort">
    <select id="price-sort" class="select-css form-control long" disabled>
      <option selected disabled class="s-title"> Sort </option>
      <option data-sorttype="des" value="price">£ Low To High</option>
      <option data-sorttype="asc" value="price">£ High To Low</option>
      <option data-sorttype="des" value="dateAdded">Newest</option>
      <option data-sorttype="asc" value="dateAdded">Oldest</option>
      <option data-sorttype="asc" value="New">New</option>
      <option data-sorttype="asc" value="original-order">Original Order</option>
    </select>
  </div>

  <!-- Filter Div -->
  <div class="filters">
    <select class="filter-select select-css short" value-group="sizes" id="sizes" disabled>
      <option selected disabled class="s-title"> Size </option>
      <option value="">All</option>
      <option value=".XS">XS</option>
      <option value=".S">S</option>
      <option value=".M">M</option>
      <option value=".L">L</option>
      <option value=".XL">XL</option>
      <option value=".XXL">XXL</option>
    </select>

    <select class="filter-select select-css long" value-group="gender" id="gender" disabled>
      <option selected disabled> Gender </option>
      <option value="">All</option>
      <option value=".Male">Male</option>
      <option value=".Female">Female</option>
      <option value=".Genderless">Genderless</option>
    </select>

    <select class="filter-select select-css short" value-group="colour" id="colour" disabled>
      <option selected disabled> Colour </option>
      <option value="">All</option>
      <option value=".Black">Black</option>
      <option value=".Blue">Blue</option>
      <option value=".Brown">Brown</option>
      <option value=".Cream">Cream</option>
      <option value=".Other">Other</option>
    </select>

    <select class="filter-select select-css long" value-group="type" id="type" disabled>
      <option selected disabled> Type </option>
      <option value="">All</option>
      <option value=".Bags">Bags</option>
      <option value=".Bottoms">Bottoms</option>
      <option value=".Co-ords">Co-ords</option>
      <option value=".Jackets">Jackets</option>
    </select>

    <select class="filter-select select-css short" value-group="brand" id="brand" disabled>
      <option selected disabled> Brand </option>
      <option value="">All</option>
      <option value=".Adidas">Adidas</option>
      <option value=".American Sports Teams">American Sports Teams</option>
    </select>

    <button type="button" id="btn-reset-filter">Reset Filter</button>
    <input type="text" id="quicksearch" class="quicksearch" placeholder="Search" / disabled>

  </div>
</div>

<div class="container">
  <ul class="grid cs-style-3">
    <div class="grid-sizer"></div>

      <li class="grid-item XS Male Beige Bags Mint">
        <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
          <figure style="background-image: URL(image.jpg);">
            <img src="https://live.staticflickr.com/65535/48297243402_ea74392016_w_d.jpg" alt="hat sale item">
          </figure>
          <div id="pro-deets"> <!-- This should not be id -->
            <h3>hat sale item</h3>
            <span id="price" class="holderpage"> <!-- This should not be id -->
              £<span class="price t-price">3</span>
            </span>
          </div>
          <div class="dateAdded">1986-10-21</div>
        </a>
      </li>

      <li class="grid-item L Female Brown Tops Worn">
        <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
          <figure style="background-image: URL(image.jpg);">
            <img src="https://live.staticflickr.com/748/20445410340_c1a0fe6a6a_w_d.jpg" alt="product no sale no new">
          </figure>
          <div id="pro-deets">
            <h3>product no sale no new</h3>
            <span id="price" class="holderpage">
              £<span class="price t-price">40</span>
            </span>
          </div>
          <div class="dateAdded">2008-9-30</div>
        </a>
      </li>

      <li class="grid-item L Female Brown Tops Worn New" data-category="New">
        <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
          <figure style="background-image: URL(image.jpg);">
            <img src="https://live.staticflickr.com/724/20624833532_4b08d803b7_w_d.jpg" alt="Skirt">
          </figure>
          <div id="pro-deets">
            <h3>Skirt</h3>
            <span id="price" class="holderpage">
              £<span class="price t-price">10</span>
            </span>
          </div>
          <div class="dateAdded">1986-10-22</div>
        </a>
      </li>

      <li class="grid-item XS Male Beige Bags Mint Sale Jackets" data-category="Sale">
        <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
          <figure style="background-image: URL(image.jpg);">
            <img src="https://live.staticflickr.com/590/20447259789_9114edf13a_w_d.jpg" alt="Jacket">
          </figure>
          <div id="pro-deets">
            <h3>Jacket</h3>
            <span id="price" class="holderpage">
              £<span class="price sale">30</span>
              <span class="price">£<span class="t-price">20</span></span>
            </span>
          </div>
          <div class="dateAdded">2018-7-30</div>
        </a>
      </li>

      <li class="grid-item XXL Male Beige Bags Mint Sale" data-category="Sale">
        <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
          <figure style="background-image: URL(image.jpg);">
            <img src="https://live.staticflickr.com/4148/4973470198_b3af07ca8c_w_d.jpg" alt="Jacket">
          </figure>
          <div id="pro-deets">
            <h3>Wallet</h3>
            <span id="price" class="holderpage">
              £<span class="price sale">30</span>
              <span class="price">£<span class="t-price">50</span></span>
            </span>
          </div>
          <div class="dateAdded">2017-5-20</div>
        </a>
      </li>

  </ul>
</div>
</body>
</html>

Фото с Flickr со ссылками:

Фото 1 - (400x267)

Фото 2 - (400x225)

Фото 3 - (400x225)

Фото 4 - (400x225)

Фото 5 - (400x250)

1 голос
/ 03 ноября 2019

Обратите внимание, что это ответ из двух частей. Проверьте часть 2 из другого ответа.

Ответ: Часть 1

Когда я прочитал ваш код, вы оказались на правильном пути. По причине того, что поиск и фильтр не работают вместе в вашем коде, проблема заключалась в том, что при инициализации $ grid вы определили функцию фильтра для $ grid. Однако, когда есть изменение с группой фильтров select, вы переопределяете этот фильтр, вызывая $ grid.isotope ({filter: filterValue}). Когда вы вызываете $ grid.isotope () с любыми настраиваемыми значениями, $ grid примет эти новые настройки.

Таким образом, ответ на ваш вопрос состоит в том, чтобы просто иметь две переменные. Один для хранения значения фильтра, а другой для сохранения значения поиска. Каждый раз, когда вызывается $ grid.isotope (), он просто использует эти два значения для фильтрации.

Есть еще несколько проблем с вашим кодом. Вам не нужно делать ценовую сортировку дважды. Это нужно сделать только один раз. Когда дело доходит до HTML, классов и идентификаторов, идентификатор должен быть только один на странице. Это означает, что вы не можете иметь два подразделения с одним и тем же идентификатором. Это, вероятно, не сломает вашу страницу, если это что-то неважно. Однако, это может сломать вашу страницу, когда дело доходит до манипулирования вашей страницей программно. Кроме того, способ использования фильтра select предназначен для получения значений из группы из двух кнопок. Но это может быть полезно для вашего случая, и я думаю, что вам, вероятно, понадобится в будущем, поскольку помимо размеров, вероятно, будут цвета и т. Д. Кроме того, при сравнении строки в JS для вашей ценовой сортировки. Лучше сравнить строки с === на равенство. Для сравнения строк в JS вы можете обратиться к этому Какой оператор равенства (== vs ===) следует использовать в сравнениях JavaScript? .

Для проектирования кода вы можете сделать так,Я думаю, способ, которым вы помещаете все это в document.ready (), заставит код работать быстрее.

Для кода ответа процедуры просты. Когда документ готов, все события, связанные с полем поиска и полем выбора, инициализируются. После этого функция filterWithHash () связывается с событием onhashchange. Затем эта функция выполняется для инициализации сетки при проверке URL-адреса для любых ассоциированных хэшей.

Для хэшей в URL, попробуйте "filter =" и "search =". Их можно использовать вместе без каких-либо проблем. Вы также можете преобразовать эту функцию в возможность принимать не только хэши, но и параметры get.

В коде также есть некоторые комментарии, которые, вероятно, могут вам помочь.

<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.js"></script>

<script>
$(document).ready(function(){
// Quick Search Regex
var qsRegex;
// Filter Value
var filterValue;
// Grid not initialize yet.
var $grid; 

// Since there is only one filter group and that is sizes. 
// It isn't necessary to be done like this. 
// Just grab the selected value everytime the sizes select is changed.
// However, this was still left like this.
$('.filter-select').on( 'change', function( event ) {
  // Hold the filter values here.
  var filters = {};
  var $select = $( event.target );
  // Get group key
  var filterGroup = $select.attr('value-group');
  // Set filter for group
  filters[ filterGroup ] = event.target.value;
  // Assign the filter value to the global filterValue
  filterValue = concatValues( filters );
  // Execute $grid.isotope() to update with current global filter value.
  $grid.isotope();
});

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

// change is-checked class on buttons
// Only need one price-sort not two
$('#price-sort').on( 'change', function() {
    var type = $(this).find(':selected').attr('data-sorttype');
    // REMEMBER TO TAKE THE CONSOLE LOG OUT IN PRODUCTION
    console.log(type);
    var sortValue = this.value;
    if( type === 'asc' ) $grid.isotope({ sortBy: sortValue , sortAscending: false});
    else $grid.isotope({ sortBy: sortValue , sortAscending: true});
    $grid.isotope({ sortBy: sortValue });
});

// use value of search field to filter
var $quicksearch = $("#quicksearch").keyup(
  debounce(function() {
    qsRegex = new RegExp($quicksearch.val(), "gi");
    // Every time qsRegex is update do $grid.isotope() to update
    // The filter with global filterValue and qsRegex
    $grid.isotope();
  })
);
// debounce so filtering doesn't happen every millisecond
function debounce(fn, threshold) {
  var timeout;
  return function debounced() {
    if (timeout) {
      clearTimeout(timeout);
    }
    function delayed() {
      fn();
      timeout = null;
    }
    setTimeout(delayed, threshold || 100);
  };
}

function getHashFilter() {
  // get filter=filterName
  var matches = location.hash.match( /filter=([^&]+)/i );
  var hashFilter = matches && matches[1];
  return hashFilter && decodeURIComponent( hashFilter );
}

function getSearchFilter() {
  // get search=filterName
  var matches = location.hash.match( /search=([^&]+)/i );
  var searchFilter = matches && matches[1];
  return searchFilter && decodeURIComponent( searchFilter );
}

/*
 * This function below can be customize to utilize not just only hashes
 * but also "Get Requests"
 */
function filterWithHash() {
  var hashFilter = getHashFilter();
  var searchFilter = getSearchFilter();
  if ( !searchFilter && !hashFilter && $grid ) {
    return;
  }
  
  // If hashFilter is there, utilize it.
  if ( hashFilter ) {
      var selectValue = $('select[id="sizes"]').find('option[value="'+ hashFilter +'"]');
      // Only search for a value if it is found within the select fields, else disregard it.
      if ( selectValue.length > 0 ){
          selectValue.prop('selected', 'selected');
          filterValue = hashFilter;
      }
  }
  // If searhFilter is there, utilize it.
  if ( searchFilter) {
    $('#quicksearch').val(searchFilter);
    qsRegex = new RegExp(searchFilter, "gi");
  }
  
  /* If $grid is not initialize, it will get initialize. 
   * This will only happen on first run.
   * One grid is initilized, everytime grid.isotope() is run
   * without any value, grid will be updated to what initilized below.
   * Thus, each later run of isotope(), the filter will look at both,
   * the searchResult and the qsRegex if they are available.
  */
  if ( !$grid ) {
    $grid = $(".grid").isotope({
              itemSelector: ".grid-item",
              layoutMode: "fitRows",
              getSortData: {
                price: '.t-price parseInt',
                category: '[data-category]',
              },
              filter: function() {
                var $this = $(this);
                var searchResult = qsRegex ? $this.text().match(qsRegex) : true;
                var selectResult = filterValue ? $this.is(filterValue) : true;
                return searchResult && selectResult;
              }
            });
  } else $grid.isotope();
}
/* 
 * Trigger filter with hash to initialize grid
 * and also to check the url for hash.
 */
filterWithHash();

// Bind the filterWithHash function to the hashchange event.
$(window).on( 'hashchange', filterWithHash );

});
</script>
</head>
<body>
<p><input type="text" id="quicksearch" placeholder="Search" /></p>

<div id="sort-filter">
  <!-- Short Div -->
  <div id="sort">
    <select id="price-sort" class="select-css form-control long">
      <option selected disabled class="s-title"> Sort </option>
      <option data-sorttype="des" value="price">£ Low To High</option>
      <option data-sorttype="asc" value="price">£ High To Low</option>
    </select>
  </div>

  <!-- Filter Div -->
  <div class="filters">
    <select class="filter-select select-css short" value-group="sizes" id="sizes">
      <option selected disabled class="s-title"> Size </option>
      <option value="*">All</option>
      <option value=".XS">XS</option>
      <option value=".S">S</option>
      <option value=".M">M</option>
      <option value=".L">L</option>
      <option value=".XL">XL</option>
      <option value=".XXL">XXL</option>
    </select>
  </div>
</div>

<div class="container">
  <ul class="grid cs-style-3">
    <div class="grid-sizer"></div>
    
      <li class="grid-item XS Male Beige Bags Mint">
        <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
          <figure style="background-image: URL(image.jpg);">
            <img src="/image2.jpg" alt="hat sale item">
          </figure>
          <div id="pro-deets"> <!-- This should not be id -->
            <h3>hat sale item</h3>
            <span id="price" class="holderpage"> <!-- This should not be id -->
              £<span class="price t-price">3</span>
            </span>
          </div>
        </a>
      </li>

      <li class="grid-item L Female Brown Tops Worn">
        <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
          <figure style="background-image: URL(image.jpg);">
            <img src="/image2.jpg" alt="product no sale no new">
          </figure>
          <div id="pro-deets">
            <h3>product no sale no new</h3>
            <span id="price" class="holderpage">
              £<span class="price t-price">40</span>
            </span>
          </div>
        </a>
      </li>

      <li class="grid-item L Female Brown Tops Worn New" data-category="New">
        <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
          <figure style="background-image: URL(image.jpg);">
            <img src="/image2.jpg" alt="Skirt">
          </figure>
          <div id="pro-deets">
            <h3>Skirt</h3>
            <span id="price" class="holderpage">
              £<span class="price t-price">10</span>
            </span>
          </div>
        </a>
      </li>

      <li class="grid-item XS Male Beige Bags Mint Sale" data-category="Sale">
        <a href="link" class="animsition-link" data-animsition-out-class="fade-out-left-lg">
          <figure style="background-image: URL(image.jpg);">
            <img src="/image2.jpg" alt="Jacket">
          </figure>
          <div id="pro-deets">
            <h3>Jacket</h3>
            <span id="price" class="holderpage">
              £<span class="price sale">30</span>
              <span class="price">£<span class="t-price">20</span></span>
            </span>
          </div>
        </a>
      </li>

  </ul>
</div>
</body>
</html>

Второй пример

Я ограничен 30000 символами. Таким образом, я удалил часть HTML из примера 2. Просто замените JS из примера 1 на тот из примера 2, чтобы запустить пример 2.

Для второго примера его процедуры почти аналогичны процедурам изпервый пример. Во втором примере всякий раз, когда пользователь выбирает какое-либо поле, связанное с операцией фильтрации сетки, выбранные значения применяются к сетке. После этого эти значения затем применяются к location.hash. Чтобы не запускать filterWithHash () и интерпретировать только что созданный хеш. Функция setHash () устанавливает переменную, которая называется gridAlreadyUpdated, в значение true, чтобы сообщать filterWithHash (), что не нужно ничего обновлять.

Функция setHash () будет интерпретировать только параметры хеш-функции, связанные соперация фильтрации. Другие хэши не учитываются.

Есть также другие комментарии, которые я написал в код, которые, вероятно, могут помочь вам.

"use strict";
$(document).ready(function(){
// Quick Search Regex
var qsRegex;
// Filter Value
var filterValue;
// sortValue & whether to sortAscending
var sortValue;
var sortAscending;
// Grid not initialize yet.
var $grid; 
// Last state of all the filters
var lastState = {};

/*
 * Parameter name for quicksearch, filter, and sort
 * Have this here so everything can easily be changed in one place.
 *
 */
var quicksearchParamName = "search";
var filterParamName = "filter";
var sortValueParamName = "sort";
var sortTypeParamName = "sorttype";


/*
 * Regexes for grabbing values from hash parameter.
 *
 */
var quicksearchRegex = RegExp(quicksearchParamName + '=([^&]+)', 'i');
var filterRegex = RegExp(filterParamName + '=([^&]+)' , 'i');
var sortValueRegex = RegExp(sortValueParamName + '=([^&]+)' , 'i');
var sortTypeRegex = RegExp(sortTypeParamName + '=([^&]+)' , 'i');

/* 
 * This variable is for the setHash() function to communicate with
 * the filterWithHash() function. 
 *
 * There isn't a need to build a hash string, update everything, and then
 * reinterprete that same hash string right after.
 * 
 * Thus, there isn't a need to run setHash() and then let filterWithHash()
 * run on hash update.
 */
var gridAlreadyUpdated = false;

// use value of search field to filter
var $quicksearch = $("#quicksearch").keyup(
  debounce(function() {
    setHash(1);
  })
);
// debounce so filtering doesn't happen every millisecond
function debounce(fn, threshold) {
  var timeout;
  return function debounced() {
    if (timeout) {
      clearTimeout(timeout);
    }
    function delayed() {
      fn();
      timeout = null;
    }
    setTimeout(delayed, threshold || 100);
  };
}

/*
 * Since there is only one filter group and that is sizes. 
 * It isn't necessary to be done like this. 
 * Just grab the selected value everytime the sizes select is changed.
 * However, this was still left like this.
 *
 */
$('.filter-select').on( 'change', function( event ) {
  // Hold the filter values here.
  var filters = {};
  var $select = $( event.target );
  // Get group key
  var filterGroup = $select.attr('value-group');
  // Set filter for group
  filters[ filterGroup ] = event.target.value;
  // Assign the filter value to the global filterValue
  filterValue = concatValues( filters );
  // Execute $grid.isotope() to update with current global filter value.
  setHash(2);
});

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

/*
 * change is-checked class on buttons
 * Only need one price-sort not two
 *
 */
$('#price-sort').on( 'change', function() {
    setHash(3, this);
});

function getHashFilter() {
  // get filter=filterName
  var matches = location.hash.match( filterRegex );
  var hashFilter = matches && matches[1];
  return hashFilter && decodeURIComponent( hashFilter );
}

function getSearchFilter() {
  // get search=filterName
  var matches = location.hash.match( quicksearchRegex );
  var searchFilter = matches && matches[1];
  return searchFilter && decodeURIComponent( searchFilter );
}

/*
 * Get the sort param. This function will always return an array with
 * 2 indexes. If both sortValue and sortType is found then it will return
 * the values for both. Value is index 1, and type is index 2. 
 * 
 * For everything else, this function will return [null, null].
 */ 
function getSortParam() {
  var valueMatches = location.hash.match( sortValueRegex );
  var typeMatches = location.hash.match( sortTypeRegex );
  var v = valueMatches && valueMatches[1];
  var t = typeMatches && typeMatches[1];
  
  if ( v && t ) return [decodeURIComponent( v ), decodeURIComponent( t )];
  return [ null, null ];
}

/*
 * This function will set the hash when one of the filtering field is 
 * changed. 
 *
 * Parameter whocall is utilize to know who is the caller. There can only
 * be one caller at a time. Whocall is utilize as int because comparing 
 * int is much faster than comparing string.
 *
 * whocall(1) = quicksearch
 * whocall(2) = filter
 * whocall(3) = sorting
 * 
 * In a secure environment any other whocall besides the 3 above should 
 * generate an error.
 */
function setHash( whocall, obj ){
  var hashes = {};
  var hashStr = "";
  /* 
   * Regex can also be utilized here to change the hash string, but for
   * example, I thought this method look more clear.
   * 
   * For performance, I haven't tested regEx vs this, so I don't know.
   * Other method are available like URLSearchParams etc... but those method
   * might not be supported by all browser.
   *
   */
  if ( location.hash ){
      /* 
       * forEach can be uitlized here, but this provide better cross platform
       * compatibitliy.
       *
       */
    let temp = location.hash.substr(1).split("&");
    for ( let i = 0; i < temp.length; i++ ){
      let param = temp[i].split("=");
      // if param[0] is 0 length that is an invalid look something like &=abc.
      if ( param[0].length === 0 ) continue;
      /*
       * if more than > 2 that is also invalid but just grab the first one anyway.
       * if exactly 1 that is something like &filter=&somethingelse. So that is an 
       * empty param.
       *
       */
        let value = param.length > 1? param[1] : '';
        // This does not check if a url receive the same parameter multiple times.
        hashes[param[0]] = value;
      }
    }
    
  /*
   * If there is a quicksearch value assign that to the hashes object.
   * If not delete quicksearch name from the hashes object if there is.
   * With this way, if there was a value for quicksearch in the hash 
   * object, it will just get overwritten. If not that index will be create.
   * The delete statement is just for cosmetic. This we turn the url back
   * to without hashes if there isn't a value. 
   * However, for faster code, this can simply be done as 
   *
   *   hashes[quicksearchParamName] = $("#quicksearch").val()
   *
   * If do like the above, whether if there is a value or not, the hash
   * parameter for quicksearch will always be built.
   *
   */
  if ( whocall === 1 ){
    // 1 : quicksearch
    if ( $("#quicksearch").val() ) hashes[quicksearchParamName] = encodeURIComponent($("#quicksearch").val());
    else delete hashes[quicksearchParamName];
    qsRegex = new RegExp($("#quicksearch").val(), "gi");
    /*
     * For lastState, if setup correctly, val will give an empty string
     * or something here.
     *
     */  
    lastState["searchFilter"] = $("#quicksearch").val();
  } else if ( whocall === 2 ){
    // 2 : filter
    /*
     * If done correctly there will always be a filter value when the user
     * choose an option
     *
     */
    hashes[filterParamName] = encodeURIComponent(filterValue);
    lastState["filterValue"] = filterValue;
  } else {
    // 3 : price sort
    /*
     * If from user selecting, without an option for resetting. If done 
     * correctly, there will always be a sortValue and sortType.
     *
     */
    lastState["sortValue"] = sortValue = obj.value;
    lastState["sortType"] = obj.selectedOptions[0].getAttribute('data-sorttype');
    hashes[sortValueParamName] = encodeURIComponent(obj.value);
    hashes[sortTypeParamName] = obj.selectedOptions[0].getAttribute('data-sorttype');;
    sortAscending = hashes[sortTypeParamName] === "asc"? true : false;
  }

  // Update the grid.
  $grid.isotope({ sortBy: sortValue , sortAscending: sortAscending});  
  /*
   * Update the hash without making filterWithHash() update the grid.
   * Join everything in hashes together into a string. Always append &
   * after a key. But when assign to "location.hash", remove the last
   * character(extra &) from the string.
   *
   */
  for ( const k in hashes ) hashStr += k + "=" + hashes[k] + "&";
  gridAlreadyUpdated = true;
  location.hash = hashStr.substr(0, hashStr.length - 1);
}

/*
 * This function below can be customize to utilize not just only hashes
 * but also "Get Requests"
 *
 */
function filterWithHash() {
  // If the grid is already updated, there is nothing to do.
  if ( gridAlreadyUpdated ) {
      gridAlreadyUpdated = false;
      return;
  }
  var hashFilter = getHashFilter();
  var searchFilter = getSearchFilter();
  var sortParam = getSortParam();
  /*
   * If the last time we access the value for the filters and it
   * is the same at this time. There isn't a point to re-execute the code
   */
  if ( $grid && lastState["searchFilter"] === searchFilter 
             && lastState["filterValue"] === hashFilter
             && lastState["sortValue"] === sortParam[0] 
             && lastState["sortType"] === sortParam[1] ) {
      return;
  }

  lastState["sortValue"] = sortParam[0];
  lastState["sortType"] = sortParam[1];
  lastState["searchFilter"] = searchFilter;
  lastState["filterValue"] = hashFilter;

  /*
   * If searhFilter is there, utilize it.
   * Else, qsRegex is reset. That is because the user could input a value into the
   * search field and then later delete that value then press enter. If that happen 
   * and we don't reset the field, the result will not be reset.
   *
   * The same goes for hashFilter below, it is just easier to use this as an example.
   */  
  if ( searchFilter ) { 
      $('#quicksearch').val(searchFilter);
      qsRegex = new RegExp(searchFilter, "gi");
  } else {
      // searchhash could be null and that is not fine with RegExp. 
      // Hence, we give it an empty string.
      $('#quicksearch').val("");
      qsRegex = new RegExp("", "gi");
  }

  /* 
   * Refer to comment of searchFilter right above 
   *
   * Also, this is for one filter group. If you need to work out to multiple 
   * filter group, you would have to split them by the . go through each
   * index, and see if they are valid values. 
   *
   * If some are valid and some are not, disregard the invalid and use the valid.
   * If none are valid, disregard all.
   * 
   */
  if ( hashFilter ) {
    var selectValue = $('select[id="sizes"]').find('option[value="'+ hashFilter +'"]');
    // Only search for a value if it is found within the select fields, else disregard it.
    if ( selectValue.length > 0 ){
      selectValue.prop('selected', 'selected');
      filterValue = hashFilter;
    }
  } else {
    // filterValue will become null or empty whichever. But that is fine.
    filterValue = hashFilter;
    $('select[id="sizes"]').prop('selectedIndex',0);
  }

  /*
   * getSortParam will always return two index. It just whether if they both have
   * values or both null.
   *
   * If sortParam is [null, null] or its values are invalid. Turn sortValue and
   * sortAscending to null.
   *
   * If sortParam is [null, null] prop the default select for select group 
   * with the id price-sort.
   */
  if ( sortParam[0] ){
    // search price sort select group to see if the hash is valid.
    var sortObj = $('#price-sort').find('option[value="'+ sortParam[0] +'"][data-sorttype="'+ sortParam[1] +'"]');
    // If hash is valid prob the field
    // Else reset the field
    if ( sortObj.length > 0 ){
      sortObj.prop('selected', true);
      sortValue = sortParam[0];
      sortAscending = sortParam[1] === "asc"? true : false;
    } else {
      sortValue = null;
      sortAscending = null;
      $('select[id="price-sort"]').prop('selectedIndex', 0);
    }
  } else {
    sortValue = null;
    sortAscending = null;
    $('select[id="price-sort"]').prop('selectedIndex', 0);
  }

  /*
   * If $grid is not initialize, it will get initialize. 
   * This will only happen on first run.
   * One grid is initilized, everytime grid.isotope() is run
   * without any value, grid will be updated to what initilized below.
   * Thus, each later run of isotope(), the filter will look at both,
   * the searchResult and the qsRegex if they are available.
   *
   */
  if ( !$grid ) {
    $grid = $(".grid").isotope({
              itemSelector: ".grid-item",
              layoutMode: "fitRows",
              getSortData: {
                price: '.t-price parseInt',
                category: '[data-category]',
              },
              sortBy: sortValue ,
              sortAscending: sortAscending,
              filter: function() {
                var $this = $(this);
                var searchResult = qsRegex ? $this.text().match(qsRegex) : true;
                var selectResult = filterValue ? $this.is(filterValue) : true;
                return searchResult && selectResult;
              }              
            });
  /*
   * When grid.isotope() is execute with sortValue, if that sortValue === null 
   * then grid.isotope() will not execute the sort parameter. That is for the 
   * isotope version of when this was first written. The code may need to 
   * be updated for future version if the behaviour of the isotope() function
   * change.
   *
   */
  } else $grid.isotope({ sortBy: sortValue , sortAscending: sortAscending});
}

/* 
 * Trigger filter with hash to initialize grid
 * and also to check the url for hash.
 */
filterWithHash();

// Bind the filterWithHash function to the hashchange event.
$(window).on( 'hashchange', filterWithHash );

});
...