jQuery Search Function - javascript

Hi i'm building a project which has a page navigation and search bar in jQuery.
I can't get my search function to work correctly and I'm not certain if it's a problem with the ID element or the each function. I'm getting the ("Sorry, no student's found!") message for anything that is or isn't a match. So i think there could be a problem with the if statement looking for a match in search function--but not sure.
I'm dynamically adding a search box to my html like this:
function appendSearchBox(){
var search = "<div class='student-search'><input id='search' placeholder='Search for students...'><button>Search</button></div>"
$(".students").after(search);
// Add click event handler
$("button").click(function() {
searchList();
});
}
this is what my html looks like for a list of students:
<div class="page">
<div class="page-header cf">
<h2 class="students">Students</h2>
</div>
<ul class="student-list">
<li class="student-item cf">
<div class="student-details">
<img class="avatar" src="https://randomuser.me/api/portraits/thumb/women/67.jpg">
<h3>iboya vat</h3>
<span class="email">iboya.vat#example.com</span>
</div>
<div class="joined-details">
<span class="date">Joined 07/15/15</span>
</div>
</li>
</ul>
And then here is the actual search function:
var listStudents = $(".student-list li");
var numStudents = listStudents.length;
function searchList() {
var matched = [];
// Obtain the value of the search input
input = $("#search").val()
// remove the previous page link section
$('.pagination').hide();
// Loop over the student list, and for each student…
listStudents.each(function(){
// ...obtain the student’s name…
var name = $(this).find("h3").val();
// ...and the student’s email…
var email = $(this).find(".email").val();
// ...if the search value is found inside either email or name…
if (name.includes(input) || email.includes(input)) {
// ...add this student to list of “matched” student
matched.push($(this).parent());
}
});
// If there’s no “matched” students…
if (matched.length === 0){
// ...display a “no student’s found” message
var message = ("Sorry, no student's found!");
$(".student-list").hide();
$(".student-list").after(message);
if (matched.length > 10) {
// ...call appendPageLinks with the matched students
appendPageLinks(matched);
}
// Call showPage to show first ten students of matched list
showPage(1, matched);
}
}
adding functions which actually show the students and add navigation
function showPage(pageNum, listStudents) {
// first hide all students on the page
pageNum = parseInt(pageNum);
listStudents.hide();
// Then loop through all students in our student list argument
listStudents.each(function(index){
// if student should be on this page number
if ((index >= ((pageNum*10)-9)) && (index <= (pageNum*10))) {
// show the student
$(this).show();
}
});
}
function getNumPages(numStudents){
numPages = Math.ceil(numStudents/10);
return numPages;
}
function appendPageLinks(numStudents) {
// determine how many pages for this student list
pages = getNumPages(numStudents);
// create a page link section
var nav = "<div class='pagination'><ul>"
for (i=1; i<pages+1; i+=1){
nav += ("<li>" + "" + i + "" + "</li>");
};
nav += ("</ul></div>");
$(".student-list").after(nav);
// define what happens when you click a link
var active = $('.pagination a').click(function(){
// Use the showPage function to display the page for the link clicked
var id = $(this).attr('id');
showPage(id,listStudents);
// mark that link as “active”
active.removeClass('active');
$(this).addClass("active");
});
}
here is how i am calling the functions:
appendSearchBox();
showPage(1, listStudents);
appendPageLinks(numStudents);
UPDATE -- I have changed the code to remove the val and put in to grab the text.
Not sure what issue is but it appears if i have a correct match--it is working (since pagination disappears) but the students do not change on the page. If there is no match then I get the error message, but the error console is saying
Uncaught TypeError: listStudents.hide is not a function
at showPage (main.js:8)
I'm not sure if this is somehow related to how I am passing the 'matched' list?

h3 and span tags have no value, but text content, so replace:
var name = $(this).find("h3").val();
// ...and the student’s email…
var email = $(this).find(".email").val();
with:
var name = $(this).find("h3").text();
// ...and the student’s email…
var email = $(this).find(".email").text();

You are using val() method to read inner text of h3 and span (email). It should be text(). Also you are appending message after the student list every time you couldn't find a student. You could have used one span tag and hide/show based on the search results.
function appendSearchBox() {
var search = "<div class='student-search'><input id='search' placeholder='Search for students...'><button>Search</button></div>"
$(".students").after(search);
// Add click event handler
$("button").click(function () {
searchList();
});
}
$(document).ready(function () {
appendSearchBox();
});
function searchList() {
var listStudents = $(".student-list li");
var numStudents = listStudents.length;
$(".student-list").show();
$("#message").hide();
var matched = [];
// Obtain the value of the search input
input = $("#search").val()
// remove the previous page link section
$('.pagination').hide();
// Loop over the student list, and for each student…
listStudents.each(function () {
// ...obtain the student’s name…
var name = $(this).find("h3").text();
// ...and the student’s email…
var email = $(this).find(".email").text();
// ...if the search value is found inside either email or name…
if (name.includes(input) || email.includes(input)) {
// ...add this student to list of “matched” student
matched.push($(this).parent());
}
});
// If there’s no “matched” students…
if (matched.length === 0) {
// ...display a “no student’s found” message
var message = ("Sorry, no student's found!");
$(".student-list").hide();
$("#message").show();
if (matched.length > 10) {
// ...call appendPageLinks with the matched students
appendPageLinks(matched);
}
// Call showPage to show first ten students of matched list
showPage(1, matched);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="page">
<div class="page-header cf">
<h2 class="students">Students</h2>
</div>
<ul class="student-list">
<li class="student-item cf">
<div class="student-details">
<img class="avatar" src="https://randomuser.me/api/portraits/thumb/women/67.jpg">
<h3>iboya vat</h3>
<span class="email">iboya.vat#example.com</span>
</div>
<div class="joined-details">
<span class="date">Joined 07/15/15</span>
</div>
</li>
</ul>
<span id="message" style="display:none;"><br/>Sorry, no student's found!</span>
</div>

Related

Trying to move completed tasks from one list to another

Here we have a tab with incompled tasks and the list is supposed to be attached to the first tab(all-taks). After the task is finished it is supposed to get deleted from first list and move to second one with completed tasks. And after you switch to second tab, all the completed ones should be there. I can't figure out the way to do it.
I hope I could get some help or some detailed explanation, 'cause I have been stuck on this for some time.
Here is an image for more clarification:
[1]: https://i.stack.imgur.com/Dy0KL.png
HTML code:
<body>
<div class="container">
<header>
<h1>To-Do List</h1>
<h4>Describe your list...</h4>
</header>
<form action="" class="todo-form">
<div class="form-wrapper">
<!-- action is where files will be sent after submitting -->
<input class="todo-input" type="text" placeholder="Add a task...">
</div>
<div class="form-wrapper">
<button class="todo-button" type="submit">Add</button>
</div>
</form>
<div class="todo-tabs">
<ul>
<li class="all-tasks active">
<span></span>All tasks (<span class="counter">0</span>)</span>
</li>
<li class="completed">
<span>Completed (<span class="counter">0</span>)</span>
</li>
</ul>
</div>
<div class="todo-list">
<div class="tabs-content" data-tab="1">
<ol class="undone-tasks"></ol>
</div>
<div class="tabs-content" data-tab="2">
<ol class="done-tasks"></ol>
</div>
</div>
</body>
JS code:
//Selectors
const todoForm = document.querySelector('.todo-form');
const todoInput = document.querySelector('.todo-input');
const todoButton = document.querySelector('.todo-button');
const tabs = document.querySelectorAll('.todo-tabs ul li');
const tabWrap = document.querySelector('.todo-tabs ul');
const undone = document.querySelector('.undone-tasks');
const done = document.querySelectorAll('.done-tasks');
//Event Listeners
tabWrap.addEventListener('click', tabs)
todoButton.addEventListener('click', addToDo);
//Functions
tabs.forEach(function (tab, tab_index) {
tab.addEventListener("click", function () {
tabs.forEach(function (tab) {
tab.classList.remove("active");
})
tabWrap.forEach(function (todoList, todoList_index) {
if (todoList_index == tab_index) {
todoList.style.display = "block";
}
else[
todoList.style.display = "none"
]
})
})
})
function addToDo(event) {
//Prevent form from submitting
event.preventDefault();
if (todoInput.value != "") {
const todoDiv = document.createElement('div');
todoDiv.classList.add('todo-div');
const inputCheckbox = document.createElement('input');
inputCheckbox.classList.add("checkbox-incompleted");
inputCheckbox.setAttribute('type', 'checkbox');
todoDiv.appendChild(inputCheckbox);
//Create li
const newToDo = document.createElement('li');
newToDo.classList.add('todo-item');
newToDo.insertAdjacentText("beforeend", todoInput.value);
todoDiv.appendChild(newToDo);
console.log(newToDo)
//Append to list
undone.appendChild(todoDiv);
//Clear todo input value
todoInput.value = "";
//Focusing after 1st input
todoInput.focus();
}
}
The commented area is what i tried to do for tabs and lists to switch, but it gives the following error:
Uncaught TypeError: tabWrap.forEach is not a function
at HTMLLIElement.<anonymous> (script.js:32:17)
(anonymous) # script.js:32
The error starts at the beggining of the tabWrap.forEach function. But I guess there could be other way to solve this
tabWrap is not useful for what you want: it is a single element. Instead, you'll want to iterate over the tab contents, which are identified by class tabs-content. It is those that you need to iterate and show or hide.
Then, to decide which contents to use, you may need to use that data-tab attribute you have in your HTML (not sure, since it is nowhere referenced).
Anyway, adapt as needed:
const tabContents = document.querySelectorAll('.tabs-content');
tabs.forEach(function (activeTab, activeIndex) {
activeTab.addEventListener("click", function (e) {
tabs.forEach(function (tab) {
tab.classList.toggle("active", tab == activeTab);
});
tabContents.forEach(function (tabContent) {
tabContent.style.display = tabContent.dataset.tab == activeIndex + 1 ? "" : "none";
});
})
})
As #Anurag Srivastava pointed out on comment, in your code this is how you get tab elements:
const tabs = document.querySelectorAll('.todo-tabs ul li');
const tabWrap = document.querySelector('.todo-tabs ul');
The method querySelector returns a single element, and querySelectorAll returns an iterable collection.
And this is why you're getting the error:
Uncaught TypeError: tabWrap.forEach is not a function
If you don't have to support internet explorer you could take a look at Element.insertAdjacentElement() to move elements from one tab to another.

Can't print correct innerHTML message within a nested function

I'm working on a recipes page where you have a series of buttons and posts that are interconnected. The buttons have names of recipe categories such as Pie and Cake. When you click on a 'Pie' button, you are only shown the posts that are categorized as 'Pie'. Both the buttons and the posts have data attributes that have their recipe category on there.
I am able to get this to work, however, I'm having issues for when you click on a recipe category button, and there are no corresponding posts. For this, I created an empty '#message' div that would output a message if there were no recipe posts found, and an empty string if there were recipe posts.
When I click on a recipe button that does have posts, I get the 'No Recipes' text in the message. Also weird that it looks like it's applying the correct message only to the last button/post which in this example is 'Cake'.
Can someone explain why this is not working? I get it's probably a scope/closure issue, but I'm unsure what's going on.
//BUTTONS
<section>
<button class="recipe_button" data-btncategory="Pie">
Pie
</button>
<button class="recipe_button" data-btncategory="Cake">
Cake
</button>
</section>
//POSTS
<div id="message"></div>
<section class="recipe" data-postcategory="Pie">
<h2>Pie Recipe</h2>
</section>
<section class="recipe" data-postcategory="Cake">
<h2>Cake Recipe</h2>
</section>
let posts = document.querySelectorAll(".recipe");
let postsArr = Array.from(posts);
let btn = document.querySelectorAll(".recipe_button");
let btnArray = Array.from(btn);
let message = document.getElementById("message");
btnArray.forEach((button) => {
button.onclick = (el) => {
let match = el.target.dataset.btncategory;
postsArr.filter(function(post, i) {
if (post.dataset.postcategory == match) {
posts[i].style.display = "grid";
<-- message not working properly -->
message.innerHTML = "";
} else {
posts[i].style.display = "none";
<-- message not working properly -->
message.innerHTML = "Sorry No Recipes Available";
}
});
}
});
look at how your filter is running. you'll always get ones that match and ones that don't - so both the if and the else code will always run
What you want to do is hide/display posts in the filter, returning true for displayed and false when hidden
That way, the resulting array length will be 0 if no match, and 1 or more if there is a match
Then another if/else after determining if there is anything displayed to show/hide the message
let posts = document.querySelectorAll(".recipe");
let postsArr = Array.from(posts);
let btn = document.querySelectorAll(".recipe_button");
let btnArray = Array.from(btn);
let message = document.getElementById("message");
btnArray.forEach((button) => {
button.onclick = (el) => {
let match = el.target.dataset.btncategory;
let found = postsArr.filter(function(post) {
if (post.dataset.postcategory == match) {
post.style.display = "grid";
return true;
} else {
post.style.display = "none";
return false;
}
}).length;
message.innerHTML = found ? "" : "Sorry No Recipes Available";
}
});
<section>
<button class="recipe_button" data-btncategory="Pie">
Pie
</button>
<button class="recipe_button" data-btncategory="Cake">
Cake
</button>
</section>
//POSTS
<div id="message"></div>
<section class="recipe" data-postcategory="Pie">
<h2>Pie Recipe</h2>
</section>
<section class="recipe" data-postcategory="Cake">
<h2>Cake Recipe</h2>
</section>
Having said all that, the message would NEVER display Sorry No Recipes Available since your buttons guarantee that there will be one displayed
Here's a straightforward way to make your idea work.
It uses an event listener with event delegation.
See the in-code comments for further clarifications.
// Identifies DOM elements
const
btnsDiv = document.getElementById("btns"),
posts = [...document.getElementsByClassName("recipe")],
message = document.getElementById("message");
// Calls `filterPosts` when btnsDiv is clicked
btnsDiv.addEventListener("click", filterPosts);
// Defines `filterPosts`
function filterPosts(event){
// Ignores irrelevant clicks
if(!event.target.classList.contains("btn")){ return; }
// Shows message while there is no match
let match = false;
message.classList.remove("hidden");
// Remembers category
const category = event.target.dataset.category;
// Iterates through recipes
posts.forEach( (post) => {
// Hides recipe until it matches
post.classList.add("hidden");
// If recipe matches, shows it and notes the match
if(post.dataset.category == category) {
post.classList.remove("hidden");
match = true;
}
});
// If any match occurred, hides message
if(match == true){
message.classList.add("hidden");
}
}
.hidden{ display: none; }
<div id = "btns">
<button class="btn" data-category="Pie">Pie </button>
<button class="btn" data-category="Cake"> Cake </button>
<button class="btn" data-category="Pasta"> Pasta </button>
</div>
<div id="message" class="hidden">Sorry No Recipes Available</div>
<div class="recipe hidden" data-category="Pie">
<h2>Pie Recipe 1</h2>
</div>
<div class="recipe hidden" data-category="Cake">
<h2>Cake Recipe 1</h2>
</div>
<div class="recipe hidden" data-category="Cake">
<h2>Cake Recipe 2</h2>
</div>

Can I assign multiple data-id values to one attribute in one element?

I use WordPress. I have an audio player on my site. When a user clicks the play button on an individual song, it will play the song. The post ID is how the player determines which song to play.
So in the DIV that wraps the button tag, I assign an attribute: data-id which is the post id of the song. My app then grabs the MP3 file that is associated with that post ID. Simple.
My play button tag is essentially this:
<div class="item item-action" data-id="1161">
<button class="btn-playpause"></button>
</div>
The important parts of my player.js file that handles the click event and plays the song is below:
Simulate the play button
// simulate the play btn
$(document).on('click.btn', '.btn-playpause, .btn-queque', function(e){
e.stopPropagation();
var self = $(this),
item = $(this).closest('.item'),
id = item.attr('data-id'),
type = item.data('user-id') ? 'user' : 'post',
play = true;
if(!player){
getItem(id, type).done(function(obj){
if(obj.status == 'success'){
mep.mepPlaylistTracks = obj.tracks;
initPlayer();
player && player.mepSelect(0, true);
}
});
return;
}
if(self.is('.btn-queque')){
play = false;
self.parent().dropdown('toggle');
}
if( self.hasClass('is-playing') ){
self.removeClass('is-playing');
player.pause();
}else{
var index = player.find(id);
if( index !== -1){
var track = player.mepGetCurrentTrack();
if(track && track.id == id && !play) return;
player.mepSelect(index, true);
}else{
getItem(id, type).done(function(obj){
if(obj.status == 'success'){
addToPlay(obj.tracks, play);
}
});
}
}
});
getItem() function
function getItem(id, type){
return $.ajax({
type : "post",
dataType : "json",
url : ajax.ajax_url,
data : {action: "ajax_music", id : id, type: type, nonce: ajax.nonce}
});
}
addToPlay() function
function addToPlay(obj, play){
if(obj.length == 1){
player.mepAdd( obj[0], play );
}else if(obj.length > 1){
if(play){
player.options.mepPlaylistTracks = obj;
player.updatemepList();
player.mepSelect(0, true);
}else{
for(var i=0; i<obj.length; i++){
player.mepAdd( obj[i] );
}
}
}
}
You can see that once I click the play button, depending on a condition, it fires the getItem() function. getItem() takes the data-id attribute defined earlier as the id variable and begins to play the song.
The addToPlay() function will add another song to the playlist if there is already an instance of the player open. And when a user clicks another play button, it will add that new song to the list and begin to play that song.
Is it possible to modify this code so that I can add multiple data-id values to my button code so that my script will recognize there is more than one song id and add those to my playlist, using the addToPlay() function?
I'd like to do something like this
<div class="item item-action" data-id="1161, 1288, 1456">
<button class="btn-playpause"></button>
</div>
The reason I want to do this, is I would like to add a feature that will allow my users to Play All Songs on the current page they're viewing. Some of our pages, like the catalog page, displays dozens of songs that fit a certain criteria and I'd like the option to play all songs on the page.
Can anyone help push me in the right direction?
Thanks.
You could store a JSON encoded array in the data-id tag. Like:
<div class="item item-action" data-id='[1161, 1288, 1456]'>
<button class="btn-playpause"></button>
</div>
Then you can parse the array, and loop over its contents, adding each id to the queue.
See an example of parsing here: https://gist.github.com/charliepark/4266921
Here I present a alternative method.
In your markup, you can set the data-id='[1234] attribute as an encoded array. You can then collect those properties and set them as the data id property on a given element using jQuery as I illustrate here:
let someElement = $('.item');
let setItems = someElement.data('id');
let thisItem = someElement.attr('data-id');
IMPORTANT: the someElement.data('id') returns the same as the someElement.attr('data-id') (except as a string) IF nothing for that element has been set with someElement.data('id',[567,890]); which sets the property id for someElement. So, once set that way
let x = someElement.data('id'); // returns the array 567,890
let org = someElement.attr('data-id'); // still returns the [1234]
This code illustrates:
let someElement = $('.item').first();
let setItems = someElement.data('id');
let thisItemS = someElement.attr('data-id');
// make an array of the string
let thisItem = JSON.parse(thisItemS);
console.log(typeof setItems, setItems, typeof thisItemS, thisItemS, typeof thisItem, thisItem);
someElement.data('id', [567, 890]);
let x = someElement.data('id'); // returns and gets the array 567,890
let org = JSON.parse(someElement.attr('data-id')); // still returns the string [1234] made into an array
// merge with no duplicates, in the other code I used .map()
let z = Array.from(new Set(x.concat(org)));
console.log(x, org, z);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class='item' data-id='[1234]'>me the item</div>
This code show a set of elements using the concepts introduced above plus:
Set some check boxes for selection
Get the values (all)
Get the values (checkbox checked)
You can combine any of this for your original purpose, but you will need to manage that addition of new items values (and subsequently how to remove perhaps) on a given element. You might even add to a <select> element so the user then can pick some to remove perhaps...
function showPicks(choicedata) {
let ul = $('#choices').append('<ul />');
//ul.remove('li');
ul.html('empty');
ul.text('choices:');
$.each(choicedata, function(index, choice) {
let c = $('<li/>').html(choice);
ul.append(c);
});
}
$(function() {
let mydata = $('.item').map(function() {
return $(this).data('id');
}).get();
$('.all-items').data("id", mydata);
showPicks(mydata); //now do what is desired
});
$('item.item-action').filter(':not(".some-items")')
.on('click', '.btn-playpause', function() {
let setItems = $(this).closest('.item').data('id');
let thisItem = $(this).closest('.item').attr('data-id');
// do what you wish here, I show them
showPicks(setItems);
});
$('.some-items').on('click', '.btn-playpause', function(event) {
let mydata = $('.item-select').filter(':checked')
.add(this) // set an additional one from the button
.closest('.item')
.map(function() {
return $(this).data('id');
}).get();
$(event.delegatedTarget).data("id", mydata);
let setItems = $(event.delegatedTarget).data('id');
// the original pick
let thisItem = $(event.delegatedTarget).attr('data-id');
// do what you wish here
showPicks(mydata);
});
.btn-playpause {
height: 2em;
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class="item item-action" data-id="[1161]">
<button class="btn-playpause" type="button"></button>
<input type="checkbox" class="item-select" />
</div>
<div class="item item-action" data-id="[1163]">
<button class="btn-playpause"></button>
<input type="checkbox" class="item-select" />
</div>
<div class="item item-action" data-id="[9961,7744]">
<button class="btn-playpause" type="button"></button>
<input type="checkbox" class="item-select" />
</div>
<div class="item item-action all-items" data-id="[42]">
<button class="btn-playpause" type="button">Play All</button>
</div>
<div class="item item-action some-items" data-id="[95]">
<button class="btn-playpause" type="button">Play selected</button>
</div>
<div id="choices">none</div>

Check each div for id based upon values within an array

I'm creating a set of tabs based upon an existing set of categories with the below JS. I need to extend this to target specific id's within the DIV id based upon values from a JS array.
$("#categories div[id^=category]:not(:first)", this).hide();
var cats = $('#categories div[id^=category]');
cats.each(function () {
var anch = $(this).find('h3 a').eq(1).clone();
anch[0].rel = this.id;
$('<li/>').append(anch).appendTo('#tabs');
});
The html:
<div id="category_1">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 1 Title
</h3>
<div>
...
</div>
</div>
<div id="category_2">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 2 Title
</h3>
<div>
...
</div>
</div>
I've got a JS array ready by adding:
var catsList = '{$cats}'; // comma separated list of numbers generated in PHP - returns 1,4,8 currently.
var catsArray = catsList.split(',');
How would I convert the below, to check for each item within catsArray ?
var cats = $('#categories div[id^=category]');
Something like
var cats = $('#categories div[id^=category_'+catsArray+']');
but obviously checking each item within the array and not the entire array as that's doing.
You could use that as IDs have to be unique on context page:
var cats = $('#category_'+catsArray.join(',#category_'));
DEMO
you probably want the each function
$.each(catsArray,function(index, item) {
var cats = $('#categories div[id^=category_'+item+']');
});
Depending on how you using this a for loop will do it also:
for (var i = 0; i < catsArray.length; i++) {
var catIndex = catsArray[i];
var cats = $('#categories div[id^=category_'+catIndex +']');
}

jquery - hide filtering option (element) if no match is found

I have the following code (also up and running on JSFIDDLE: http://jsfiddle.net/DSuua/12/)
It sorts products based on price matches. I want to hide a filter option from the left panel list altogether on page load if there are no products within that price range (versus showing the price range no matter what, like I do now).
So, as an example, if there are no products within the price range of 10 - 20, then hide the "$10 - $20" on the left panel since it's irrelevant and it just shows an annoying "no matches found" message to user.
Right now the filtering is performed on the click event. Before the filtering is done, it needs to first check the list attribute values (name, title) agains the product price values (lowest & highest price) and if no matching ranges are found, then hide those filter items from the list.
I'm limited to this solution on the client-side only.
Also, any overall tips for improving this code are welcome.
HTML:
<ul id="filterByPrice">
<li><span class="section-header">PRICE</span></li>
<li>Any Price</li>
<li>Under $10</li>
<li>$10 - $20</li>
<li>$20 - $30</li>
<li>$30 - $40</li>
<li>$40 - $50</li>
<li>Over $50</li>
</ul>
<ul id="products">
<li>
Product One<br>
<span id="lowestPriceRange">0</span>
<span id="highestPriceRange">9</span>
</li>
<li>
Product Two<br>
<span id="lowestPriceRange">20</span>
<span id="highestPriceRange">29</span>
</li>
<li>
Product Three<br>
<span id="lowestPriceRange">30</span>
<span id="highestPriceRange">39</span>
</li>
<div id="nothingFound" style="display:none;">
Nothing found
</div>
</ul>
CSS:
#filterByPrice, #products {
border:1px solid #ccc;
padding:10px;
width:100px;
margin:10px;
float:left;
position:relative;
}
JS:
var noProductMatches = $('#nothingFound');
$('#filterByPrice li a').click(function()
{
noProductMatches.hide();
var priceRangeSelectedItem = $(this).html().toLowerCase();
var minSelectedPrice = parseInt( $(this).attr("name") );
var maxSelectedPrice = parseInt( $(this).attr("title") );
var productContainer = $('#products li');
if (priceRangeSelectedItem == 'any price')
{
productContainer.fadeOut("slow");
productContainer.delay(100).fadeIn("slow");
}
else
{
var results = productContainer
.fadeOut(100)
.delay(100)
.filter(function()
{
var minProductPrice = parseInt( $(this).find("#lowestPriceRange").html().toLowerCase() );
var maxProductPrice = parseInt( $(this).find("#highestPriceRange").html().toLowerCase() );
return (minProductPrice >= minSelectedPrice && maxProductPrice <= maxSelectedPrice);
})
.each(function(index, item)
{
$(item).fadeIn("slow");
});
if (results.length == 0)
{
noProductMatches.fadeIn();
}
}
});
​
​
First, you should only have one HTML element of a given id on a page, so the highestPriceRange and lowestPriceRange should be classes. Second, this sounds like something that would be easier to do server side, but if you have to do it client side this should do the trick:
var hidePrices = {};
hidePrices[0] = true;
hidePrices[10] = true;
hidePrices[20] = true;
hidePrices[30] = true;
hidePrices[40] = true;
hidePrices[50] = true;
$('#products').find('span').each(function(i, el){
// round price down to nearest 10s
var key = parseInt(Math.floor($(this).html() / 10) * 10, 10);
hidePrices[key] = false;
});
$('#filterByPrice').find('li').each(function(i, el){
if (hidePrices[Number($(this).find('a').attr('name'))]) {
$(this).hide();
}
});
See demo

Categories