I am developing a javascript filtering of some results and have some difficulties..
Here is the problem...
Suppose that we have some criteria
Manufacter (Trusardi, Calvin Kein, Armani...)
Color (red, blue, black...)
OtherFeatures (goretex, blah, blah...)
Each feature is displayed as a checkbox...
The problem is that i want to disable the checkboxes if its selection would lead to no results..For example Armani's products may have color blue only so checking armani should disable the black and red,but other manufactors shouldnt be disabled... as checking them should provide a result...
Here is the code so far
results = $("#products li");
results.hide();
var filtersGroup = $("#filters li.filtersGroup");
$("#filters li.filtersGroup a").removeClass("disabled");
filtersGroup.each(function(index) {
var classes = "";
$(this).find("a.checked").each(function(index) {
classes = classes + "." + $(this).attr("id") + ",";
});
if (classes == "") return true;
results = results.filter(classes.substr(0, classes.length - 1));
//periorismos
filtersGroup.not($(this)).each(function(index) {
$(this).find("a").each(function(index) {
if (results.filter("." + $(this).attr("id")).length <= 0) {
$(this).removeClass("checked").addClass("disabled");
}
});
});
});
Although it filters them successfully ,the disabling isnt always correct. for example to reproduce the problem ,if you choose all the manufactors and then choose a color manufactors would be disabled but colors not at the first time..
One solution i figured is to create multiple results which would simulate all the next possible check.(if 16 features and 4 checked means 12 possible other checked ..
But i think that this approach sucks... Any other idea?
You could add a class for each manufacturer, colour, etc. then simply disable by class. I assume here there's a results hash keyed off manufacturer:
results['Trusardi'] = 5
results['Armani' = 0
..then:
$("#filters li.filtersGroup a").addClass("disabled");
foreach (m in manufacturers) {
if (manufacturers[m] > 0) {
$("#filters li.filtersGroup a." + m).removeClass("disabled");
}
}
etc.
You have a faceted search problem - where many combinations produce no results. Instead of disabling, I suggest calculating and showing how many results results would appear if a given criterion is selected. Then with each click, executing that algorithm again. So your search would like very similar to newegg.com:
http://www.newegg.com/Store/SubCategory.aspx?SubCategory=10&name=Desktop-PCs
Depending on how much data you're dealing with you might want to consider using solr.
I think that i found the solution.. It was simpler than i thought... I was trying to find a solution all day long and it was simpler than i thought..
Here it is....
results=$("#products li");
results.hide();
var groupClasses=[];
var groupsChecked=0;
var filtersGroup=$("#filters li.filtersGroup");
$("#filters li.filtersGroup a").removeClass("disabled");
filtersGroup.each(function(index) {
var classes="";
$(this).find("a.checked").each(function(index) {
classes=classes+ "." + $(this).attr("id") +",";
});
groupClasses[groupClasses.length]=classes;
if(classes=="") return true;
groupsChecked++;
results=results.filter(classes.substr(0,classes.length-1));
});
//disable
var gi=0;
filtersGroup.each(function(index) {
if( ! (groupsChecked<=1 && groupClasses[gi]!=""))
{
$(this).find("a").not(".checked").each(function(){
if (results.filter("." + $(this).attr("id")).length <= 0) {
$(this).removeClass("checked").addClass("disabled");
}
});
}
gi++;
});
It seems to be correct. I amnot sure though but testing it seems ok..
Can't you just compute the results for every unchecked option and compare them to the current products that suffice. (I'm not very familiar with jQuery so I wouldn't know how...)
Related
Here's the JSFiddle of my work: https://jsfiddle.net/pb23Ljd8/5/
I use Bootstrap nav-pills to show all products and categorized too like this:
And I based my checkboxes from here: http://bootsnipp.com/snippets/featured/fancy-bootstrap-checkboxes
I count the number of products checked in between the tabs like this:
jQuery(document).ready(function($) {
jQuery(".select-product").change(function() {
jQuery(".counter").text(jQuery("[type='checkbox']:checked").length);
});
});
But the glyphicon check icons doesn't appear on the second and third tabs for some reason. But when I click the products on the second and third, it increases the counter and also when I view it on the first tab, it is checked.
I just need the products to also be visibly checked on the second and third tabs and not only on the first one so it's not confusing for the user.
Ideas, anyone?
Edit: I fetch the list of products from CMS so it's dynamic. I now understand that the duplication of IDs is causing the problem.
Before we try and resolve this issues, we should break it down and see what the actual problem is.
First, let's check if we remove the content from tab 1b is the issue still present?
Nope, if we remove the checkboxes from the first tab, the checkboxes function normally on the second and third.
Fiddle #1
What if we change the id of the checkboxes (remember ids should be unique).
Notice how Book #1 now works if we change the first checkbox's id to 1a.
Fiddle #2
So now we "know" the issue is likely due to the fact that we are using checkboxes with the same id value (ref). The "issue" is now:
How do we check multiple checkboxes if one is checked
(or something like that)
Here's what I would do:
assign all "like" checkboxes the same class (ex. Book #1 checkboxes will have class b1)
use jQuery/javascript to make sure all that all "like" checkboxes, check and uncheck in unison
Working Example
EDIT
Dynamic values for the classes can be achieved by putting the IDs as classes so the similar products would match. These can be passed to JS like this assuming that $products_id_array is a PHP array that contains all the classes needed.
var productIDs = <?php echo json_encode($products_id_array) ?>;
and then creating the snippet of jQuery code on the fiddle like this
productIDs.forEach(function(val, key) {
jQuery('.' + val).on('change', function(){
jQuery('.' + val).prop('checked',this.checked);
});
})
Try this JS, This will work
jQuery(".select-product").change(function() {
var checkValue = jQuery(this).prop('checked');
$('.select-product#' + jQuery(this)[0].id).each(function() {
if (checkValue == true) {
jQuery(this).prop('checked', true)
} else {
jQuery(this).prop('checked', false);
}
});
var uniqueId = [];
jQuery("[type='checkbox']:checked").each(function() {
uniqueId.push(jQuery(this)[0].id);
});
Array.prototype.getUnique = function() {
var u = {},
a = [];
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
jQuery(".counter").text(uniqueId.getUnique().length);
});
I have 3 checkboxes, and I want all combinations of those checkboxes to display a different result, but I cannot work out how to do so. It feels like there's a simple way of doing this that I'm missing.
Here's the Frankenstein monster-code I have so far, which doesn't do what I'd like it to. The aim is that the following code sees that a user has has checked both the "webcam" (#checkWebcam) and "chat" (#checkChat) boxes, and that a different download link is being displayed based on that selection combo...
jQuery('#checkWebcam, #checkChat').change(function() {
var isChecked = jQuery('#checkWebcam, #checkChat').is(':checked');
if(isChecked)
jQuery('div.strip.download').html('Download');
else
jQuery('div.strip.download').html('Download');
});
Could anyone help me with how to actually achieve this aim? Thank you in advance.
This will do an OR operation,
var isChecked = jQuery('#checkWebcam, #checkChat').is(':checked');
what you basically need is to perform an AND opereation here, so use
var isChecked = jQuery('#checkWebcam').is(':checked') && $('#checkChat').is(':checked');
You can try this code :
if ( $("#checkWebcam:checked, #checkChat:checked").length == 2 ) {
//your code
}
var isChecked = jQuery('#checkWebcam, #checkChat').is(':checked'); will return true if either one or both are selected.
Modify your code as
jQuery('#checkWebcam, #checkChat').change(function() {
var boxs = jQuery('#checkWebcam, #checkChat');
var checked = boxs.filter(':checked'); //Filter checked checkboxes
if(boxs.length == checked.length){ //if length are same
alert('both are checked')
}else{
alert('both are not checked')
}
});
DEMO
I have two unordered lists, each filled with list items that have a DYNAMIC class name. When I say "dynamic" I mean they are not generated by me, but they don't change once the lists have been created. These class names are id's I'm getting from an API, so they're just random numbers. A simple example would be something like...
<ul class="listA">
<li class="123"></li>
<li class="456"></li>
<li class="789"></li>
</ul>
<ul class="listB">
<li class="789"></li>
<li class="101"></li>
<li class="112"></li>
</ul>
What I'm trying to do is compare the two lists, and have any matches be highlighted, in this case the items with the class "789" would match. When I say highlighted, I just mean I'll probably apply some css after a match is found, like maybe a background color or something (not too important yet). The problem really lies in the fact that the lists can be somewhat long (maybe 50 items) and the classes are just random numbers I don't choose, so I can't do any specific searches. Also, there will most likely be cases with multiple matches, or no matches at all.
I'm pretty new to jQuery, so there may be a fairly simple answer, but everything I find online refers to searching by a specific class, such as the .find() method. If anyone needs more info or a better example, I'll be happy to give more info, I'm just trying to keep it simple now.
Thanks so much in advance!
var $first = $('ul.listA li'),
$second = $('ul.listB li');
$first.each(function(){
var cls = this.className,
$m = $second.filter(function(){
return this.className === cls;
});
if ($m.length) {
$(this).add($m).addClass('matched');
}
});
http://jsfiddle.net/b4vFn/
Try it this way:
$("ul.listA li").each(function(){
var listAval = $(this).attr('class');
$("ul.listB li").each(function(){
if(listAval == $(this).attr('class')){
//matched..
return false; //exit loop..
}
}
}
you can find the code here: jsFiddle
var listA=$('.listA li')
var listB=$('.listB li')
listA.each(function(){
var classInA=$(this).attr('class');
listB.each(function(){
var classInB=$(this).attr('class');
if(classInA === classInB){
console.log(classInA);
//now you found the same one
}
})
})
Demo at http://jsfiddle.net/habo/kupd3/
highlightDups();
function highlightDups(){
var classes = [] ;
$('ul[class^="list"]').each(function(k,v){
//alert(v.innerHTML);
$($(this).children()).each(function(nK,nV){
// alert($(this).attr("class"));
classes.push($(this).attr("class"));
});
});
hasDuplicate(classes);
}
//Find duplicate picked from fastest way to detect if duplicate entry exists in javascript array?
function hasDuplicate(arr) {
var i = arr.length, j, val;
while (i--) {
val = arr[i];
j = i;
while (j--) {
if (arr[j] === val) {
// you can write your code here to handle when you find a match
$("."+val).text("This is Duplicate").addClass("match");
}
}
}
}
A slightly less verbose variant of Nix's answer:
$("ul.listA li").each(function(){
var a = $("ul.listB li").filter("." + $(this).attr('class'));
if (a.size()) {
a.add($(this)).css("background", "red");
}
});
Okay so I have two drop downs I want the selected item in dropdown one to be hidden in dropdown two and vice versa.
I have done the following so far can't seem to figure out the final step was hoping for a hand.
What I have currently is I have two lists that append to the dropdowns from and to, these list are looped over and append values to the dropdownlists, I then check for change event and when this occurs I remove values from a dropdown based on its index.
I am currently removing on selectedIndex, I want to remove on selectedValue rather then index but could not grasp that either.
<script type="text/javascript">
var fromCurrencies = {
FRO : 'Convert this currency',
AUD : 'AUD, Australian Dollar',
NZD : 'NZD, New Zealand Dollar',
EUR : 'EUR, Euro',
USD : 'USD, United States Dollar',
};
var toCurrencies = {
TOC : 'To this currency',
AUD : 'AUD, Australian Dollar',
NZD : 'NZD, New Zealand Dollar',
EUR : 'EUR, Euro',
USD : 'USD, United States Dollar',
};
$(document).ready(function () {
var ddFrom = $(".ddConvertFrom");
$.each(fromCurrencies, function (val, text) {
ddFrom.append(
$('<option></option>').val(val).html(text)
);
}); /*End ddFrom loop*/
var ddTo = $(".ddConvertTo");
$.each(toCurrencies, function (val, text) {
ddTo.append(
$('<option></option>').val(val).html(text)
);
}); /*End ddTo loop*/
}); /*End document.ready function*/
function doAction(){
if ($('.ddConvertFrom').val == "" || $('.ddConvertFrom').get(0).selectedIndex == 0) {
//Do nothing or hide...?
} else {
/*Hide selected value from other dropdown*/
var index = $('.ddConvertFrom').get(0).selectedIndex;
$('.ddConvertTo option:eq(' + index + ')').remove();
}
}
</script>
The html:
<div class="selectstyler">
<asp:DropDownList ID="ddConvertFrom" OnChange="doAction()" CssClass="ddConvertFrom" runat="server"></asp:DropDownList>
</div>
<div class="selectstyler">
<asp:DropDownList ID="ddConvertTo" CssClass="ddConvertTo" runat="server"></asp:DropDownList>
</div>
Purely for completeness to add to an already working answer from undefined.
To address the vice versa and the extended issue of re-adding those values including auto-selecting the previous value if it exists see: DEMO
See below for the changes I made to your original scripts.
I'm sure the code below can be optimised on several levels but I only tried to get it working first. Making it pretty I leave to you :)
To start I re-factored your code so the values are attached in their respective methods.
The dropdowns are now fully cleared before the values are re-added.
First though we record the value of the currently selected option to ensure we can re-select it if it exists. Adds a more dynamic feel to it and saves the user form re-selecting manually.
See example of attaching values to the from-dropdown:
function attachFromValues() {
var ddFrom = $(".ddConvertFrom");
var selectedValue = ddFrom.val();
var selectedIndex = 0;
ddFrom.html("");
var index = 0;
$.each(fromCurrencies, function(val, text) {
ddFrom.append(
$('<option></option>').val(val).text(text));
if (selectedValue == val) {
selectedIndex = index;
}
index++;
}); /*End ddFrom loop*/
if (selectedIndex > 0) {
ddFrom.get(0).selectedIndex = selectedIndex;
}
};
I also re-factored the code which removes the value in the other dropdown calling the new re-factored methods to re-attach all values first before removing the specific value. That makes sure you do not end up with an empty dropdown after while.
See example of the change event of the to-dropdown:
(taken from undefined's answer, only added call to re-populate)
$('select.ddConvertTo').change(function() {
if ($('.ddConvertTo').val == "") {
//Do nothing or hide...?
} else { /*Hide selected value from other dropdown*/
attachFromValues();
var txt = $(this).val();
$('.ddConvertFrom option[value=' + txt + ']').remove();
}
})
For the complete code changes check the linked DEMO
Edit (25-Jun-2012)
Updated code as I noticed an inconsistency whereby the currencies did not re-set correctly if the default selection (index 0) was made. The latest version of this code now re-binds the currencies correctly.
try this and instead of using onChange attribute you can use change event handler.
$('select:first').change(function(){
/*remove selected value from other dropdown*/
var txt = $(this).val();
$('.ddConvertTo option[value=' + txt + ']').remove();
})
http://jsfiddle.net/ue4Cm/1/
I have child divs that I'm trying to sort based on a jquery .data() value that I give them that is just a single number. This code works perfectly, but only once, after that I can't figure out how the heck it's sorting them. Here is a simplified version:
var myArray = $('#container div').get();
myArray.sort(function(x,y) {
return $(x).data('order') - $(y).data('order');
});
$('#container').empty().append(myArray);
I've tried so many other different methods of sorting, other plugins, etc., and I can't get anything to work right. This is as close as I can get. I just have this running on a jquery change event.
Here is the whole thing in case I'm doing something stupid elsewhere:
$('#attorneyFilter').change(function() {
//get array of links for sorting
var myArray = $('#attorneyBlocks div').get();
var selectedArea = $(this).val();
//sort alphabetically when "all" is selected
if (selectedArea == 'all') {
$('#attorneyBlocks div').show();
myArray.sort(function(a,b) {
return $(a).text() > $(b).text() ? 1 : -1;
});
//filter attorneys based on practice area and then assign its order# to the div with data, getting all values from the div's class
} else {
$('#attorneyBlocks div').hide().each(function() {
var attorneyArea = $(this).attr('class').split(', ');
for (var i=0;i<attorneyArea.length;i++) {
var practiceArea = attorneyArea[i].split('-');
if (selectedArea == practiceArea[0]) {
$(this).show().data('order',practiceArea[1]);
}
}
});
//sort based on order, the lower the number the higher it shows up
myArray.sort(function(x,y) {
return $(x).data('order') - $(y).data('order');
});
}
//append order back in
$('#attorneyBlocks').empty().append(myArray);
});
And a link to the page in question
Here's a jsFiddle with this working using .detach() instead of .empty() to keep the data.
http://jsfiddle.net/shaneblake/Tn9u8/
Thanks for the link to the site, that made it clear.
It seems to me you never clear out the data from the prior time. You hide everything but maybe something like this will solve your problem (here I set everything hidden to the bottom, you can clear it or use a different value -- as long as it is not the same as any sort key):
$('#attorneyBlocks div').hide().data('order',999999).each(function() {
var attorneyArea = $(this).attr('class').split(', ');
for (var i=0;i<attorneyArea.length;i++) {
var practiceArea = attorneyArea[i].split('-');
if (selectedArea == practiceArea[0]) {
$(this).show().data('order',practiceArea[1]);
}
}
});
Also, the code on the server is missing the 2nd line you have above:
var myArray = $('#attorneyBlocks div').get();
The problem is the change event is tied to the original items. After the sort you make all new items. They don't have any event tied to them. You will need to use .live()
Eventually figured it out, the data values from hidden divs were screwing with my sorting, so I changed my sorting code to only pay attention to :visible divs and that did the trick. Doh! Thanks for your help everyone.