Обратите внимание, что это ответ из двух частей. Проверьте часть 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 );
});