同位素过滤器、排序、搜索和Url哈希



我正在使用同位素对产品页面进行排序、筛选和搜索。到目前为止还不错。我被困在几个方面。我还没能在网上找到一个具有我所需要的功能的确切组合的例子,所以需要一些帮助。

简而言之,我有多个筛选产品的下拉列表,选择价格排序顺序和一个快速搜索输入字段。所有的工作,但我有一些需要的补偿。

要做的事:

筛选后搜索不起作用。我需要它来处理过滤器。

在价格选择下拉列表中添加按"销售"one_answers"新订单"排序。

添加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部分

当我读到你的代码时,你是在正确的轨道上。由于搜索和筛选在代码中不能协同工作的原因,问题在于,当您初始化$grid时,您为$grid定义了一个筛选函数。但是,当选择筛选器组发生更改时,您可以通过调用$grid.tiope[{filter:filterValue}]重新定义该筛选器。当您使用任何可配置的值调用$grid.isocope()时,$grid将接受这些新配置。

因此,你的问题的答案就是有两个变量。一个用于存储筛选值,另一个用于保存搜索值。无论何时调用$grid.isotrope(),它都只使用这两个值进行过滤。

您的代码还有其他一些问题。你不需要做两次价格排序。这只需要做一次。当涉及到HTML、类和id时,id在一个页面中应该只出现一个。这意味着,你不可能有两个具有相同id的分区。如果它是不重要的东西,它可能不会破坏你的页面。然而,当涉及到以编程方式操作页面时,这可能会破坏页面。除此之外,使用过滤器选择的方式是为了从两个按钮组中获取值。但它可以用于您的情况,我想您将来可能需要它,因为除了尺寸,可能还会有颜色等…此外,在比较JS中的字符串以进行价格排序时。最好将字符串与===进行比较以获得相等性。对于JS中的字符串比较,您可以参考以下内容:JavaScript比较中应该使用哪个等于运算符(==vs===)?。

对于代码设计,您可以这样做。我认为把所有类似的东西放在document.ready()中的方式会让代码运行得更快。

对于答案代码,例程很简单。当文档准备就绪时,将初始化与搜索字段和选择字段关联的所有事件。之后,filterWithHash()函数与onhashchange事件绑定。然后执行该函数来初始化网格,同时检查URL中是否有任何关联的哈希。

对于URL中的散列,请尝试"filter="one_answers"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个字符。因此,我从示例2中删除了HTML部分。只需将示例1的JS替换为示例2中的JS即可运行示例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 );
});

答案:第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)

最新更新