I am attempting to build a simple narrow by filter using given key word buttons on an otherwise static list of items.
The buttons are in an unordered list and when selected get the class ".selected-tag-button" added to them.
The items are divs with class ".item" and get class ".included-item" when they are active. Inside the div is another UL with list items that contain key words that match the text node on the buttons.
Right now it is working, except, instead of using "buttonName" which contains only the key word for the clicked button, I would like to use "buttonArray" which contains an array of all the selected key words.
I assume I will need some kind of function, but I am not sure where to start. If more than one are selected I want the result to be limited to only items that contain ALL of the selected key words. All of the solutions I have been able to figure out will return the divs that contain ANY of the key words in the array.
$(document).ready(function() {
$("li.tag-button").on("click", function() {
// Toggle button
$(this).toggleClass("selected-tag-button");
// Remove included-item class from all items
$(".item" ).removeClass("included-item");
// Pass the button text into variable
var buttonName = $(this).text().slice(2);
// Create array with button text for all active buttons
var buttonArray = $(".selected-tag-button").map(function() {
return $(this).text().slice(2);
}).get();
console.log(buttonArray);
// Add included-item class to divs containing the button text
$('li:contains("' + buttonName + '")').parents().parents().addClass("included-item");
// If all buttons are inactive, add included-item class to all items
if ($(".selected-tag-button").length == 0 ) {
$(".item" ).addClass("included-item");
}
});
});
Consider this fiddle:
http://jsfiddle.net/6qavvth8/
for(i=0; i<buttonArray.length;i++){
contains += ':contains("' + buttonArray[i] + '")';
}
$('ul' + contains).parents().addClass("included-item");
Loop through your button array to build your jquery selector and keep adding :contains()
Slight modification of #bingo's solution. Works perfectly, thanks.
$(document).ready(function() {
$("li.tag-button").on("click", function() {
// Toggle button
$(this).toggleClass("selected-tag-button");
// Remove included-item class from all items
$(".item" ).removeClass("included-item");
// Create array with button text for all active buttons
var buttonArray = $(".selected-tag-button").map(function() {
return $(this).text().slice(2);
}).get();
// Add included-item class to divs containing the button text
var contains = "";
for(i = 0; i < buttonArray.length; i++){
contains += ':contains("' + buttonArray[i] + '")';
}
$('ul' + contains).parents().addClass("included-item");
// If all buttons are inactive, add included-item class to all items
if ($(".selected-tag-button").length == 0 ) {
$(".item" ).addClass("included-item");
}
});
});
Related
I am dynamically creating li with javascript, I want to add a close button to each li element created dynamically to delete the li element on click of the close button.This is my code so far:
function addNew(){
// get value from input field
var taskName = document.getElementById('task-name').value;
// innerHTML to be inserted inside li
var fullText = taskName + '<span class = "close" onclick =
"addListener(this)">×</span>';
// calling create function from Element object
Element.createNew('li','className','tasks',0,fullText);
}
// remove function
function addListener(e){
e.parentNode.parentNode.removeChild(e.parentNode);
}
The problem is the remove function removes the last li instead of li being clicked.
Here is the JSFiddle of the problem.
store all the list items in an array.
//suppose your list items have a class name 'lists'
//create a global var
var lis = document.getElementsByClassName('lists');
initially it'll be empty,
so in your add method(in which you're appending the new list item to ul,
push the new list item in the lists array.
and in the addEvent(e) method , loop around every element in the lists array
function addEvent(e){
for(var i=0; i<lists.length; i++){
if(lists[i] === e){
//remove the lists element by using lists[i] instead of e
// and remember to pop the lists[i] and resize the lists array
}
}
I am creating a recipe tool that takes a user's input via selected checkboxes. See here: zelda.wptoolkit.us
Part One:
I have a script that will create an array of slugs based off of the selected input values. When a user clicks a checkbox, the associated slug is added to an array called checkedAttr.
<script>
var checkedAttr = [];
$('#wp-advanced-search :checkbox').change(function()
{
checkedAttr = [];
$('#wp-advanced-search :checkbox').each(function(i, item){
if($(item).is(':checked'))
{
checkedAttr.push($(item).val());
}
});
console.log("checkedAttr:", checkedAttr);
});
</script>
Part Two:
I am trying to use the code below to .addClass to any links that contain a slug found in the array from part one.
The link structure: http://zelda.wptoolkit.us/tag/any-crab/
The code:
<script type="text/javascript">
jQuery(function() {
jQuery('#wpas-results-inner > div > div > p > a[href^="/tag/' +
location.pathname.split("/") this.checkedAttr[0] + '"]').addClass('active');
});
</script>
What I am aiming to do is target the links in each card, then add a class to the links whose slug is found in my array. The end goal is to highlight checked 'ingredients' and fade out ingredients that haven't been checked.
I am not exactly sure how to make this function check my array for each slug, I would love to learn what steps are needed to accomplish this!
I am also not certain if my jQuery CSS path is correctly targeting the links
Thanks for any insights!
If you are looking for something like when select particular checkbox, all the card related to that checkbox should be enabled(lets say apply any class active) and once you dis-select the checkbox, we need to remove that particular class from all related checkbox??
you can check the given fiddle.
https://jsfiddle.net/stdeepak22/x1L95dw3/1/
var checkedAttr = [];
$('#checkBoxForCategory :checkbox').change(function()
{
var cardCategory = $(this).val();
var allCards = $('.myCard[card-category=' + cardCategory + ']');
if($(this).is(':checked'))
{
//add the selected category to array
checkedAttr.push(cardCategory);
//add the `active` class for all the card belongs to selected category
$(allCards).each(function()
{
$(this).addClass('active');
});
}
else
{
//remove from array
var index = checkedAttr.indexOf(cardCategory);
checkedAttr.splice(index, 1);
//remove the class for all the card belongs to selected category
$(allCards).each(function()
{
$(this).removeClass('active');
});
}
console.log("checkedAttr:", checkedAttr);
});
I am trying to have the user check the boxes they are interested in getting resources for and then click the button to get a list of those resources that are hyperlinked to those resources. The hyperlinks (ul id="results” in HTML) are hidden until they called upon by the button “Get Resources”.
Plus I would like to add text to it before results saying “You have indicated an interest in:” (line break) then a listing the hyperlinks (line break) “Please click on the links to learn more”. If no check box is selected the div id=“alert” displays, which I got to work.
I think I am very close, I just can’t seem to get the list of resources.
Here is a link to my coding:
JSFiddle Code sample
$(document).ready(function() {
$('#alert').hide();
$('#results > li').hide();
/* Get the checkboxes values based on the parent div id */
$("#resourcesButton").click(function() {
getValue();
});
});
function getValue(){
var chkArray = [];
/* look for all checkboxes that have a parent id called 'checkboxlist' attached to it and check if it was checked */
$("#checkBoxes input:checked").each(function() {
chkArray.push($(this).val());
});
/* we join the array separated by the comma */
var selected;
selected = chkArray.join(',') + ",";
/* check if there is selected checkboxes, by default the length is 1 as it contains one single comma */
if(selected.length > 1){
// Would like it to say something before and after what is displayed
$('#results > li.' + $(this).attr('value')).show();
} else {
$('#alert').show();
}
}
I'd ditch the selected variable and just check the chkArray contents against the list item classes like:
function getValue() {
var chkArray = [];
/* look for all checkboxes that have a parent id called 'checkboxlist' attached to it and check if it was checked */
$("#checkBoxes input:checked").each(function () {
chkArray.push($(this).val());
});
$('#results li').each(function () {
if ($.inArray($(this).attr('class'), chkArray) > -1) $(this).show()
else($(this).hide())
})
/* check if there is selected checkboxes, by default the length is 1 as it contains one single comma */
if (!chkArray.length) {
$('#alert').show();
//alert("Please at least one of the checkbox");
}
}
jsFiddle example
I found a straightforward way of achieving what you want. DEMO: https://jsfiddle.net/erkaner/oagc50gy/8/
Here is my approach: I looped through all checkboxes. This way I could get the index of the current item in the original list, i, and use this index to display the corresponding item in the second list. I filter the checked items by using .is(':checked') condition, and then added them item to the array:
function getValue() {
var chkArray = [];
$("#checkBoxes input").each(function (i) {//now we can get the original index anytime
if($(this).is(':checked')){//is the item checked?
chkArray.push($(this).val());//if so add it to the array
var selected;
selected = chkArray.join(", ");
if (selected.length) {
$('#results').find('li').eq(i).show();//show the corresponding link by using `i`
} else {
$('#alert').show();
}
}
});
}
Last thing in your $(document).ready function, add:
$("#checkBoxes input:checkbox").click(function() {
$('li.' + $(this).val().replace(/ /g, '.')).show()
});
JSFiddle
Explanation:
On document ready, add a click handler to the checkboxes that shows the corresponding hidden list item below. The tricky thing here is the spaces in the list names. This makes each word a separate classname, so simply combine the list names with a dot . which results in a sequential classname call in jQuery.
By using <li class="Fitness & Recreation"> as a list item classname, you are giving this item 3 classnames: Fitness, &, and Recreation. In jQuery you select elements with multiple classnames by including each name preceded by a dot .. For example, selecting a list item element with the classnames foo, bar, and baz:
$('li.foo.bar.baz').show()
In the case of <li class="Fitness & Recreation">:
$('li.Fitness.&.Recreation').show()
Since these values are stored in the value attribute of the checkboxes we use jQuery to pull these values: $(this).val(), replace the spaces with dots: .replace(/ /g, '.'), and concatenate the result to the li. portion to access the appropriate list item.
I'm trying to add a second class to a group of buttons using a for loop in jQuery, with the second class set to the number of the current iterator(i). The first class for each button is assigned to "users". I want each button to have a dynamic second class so that when clicked, I can use its second class as an index to access a particular key in an object.
The problem I'm having is that each button has its second class set to 0. "users" is an array with length 4, so the buttons should have as their second class 0,1,2,3 in order.
$(document).ready(function(){
for(var i = 0; i < users.length; i++) {
var $currentUser = $('<button class="users '+ i +'"></button>');
$currentUser.text(users[i]);
$currentUser.appendTo('.userList');
}
$(".users").click(function() {
alert($(".users").attr('class').split());
// Alert returns "users 0" for each button.
});
});
The alert at the bottom is just a placeholder for now to check that the classes are set correctly. Thanks!
change code to below . change $(".users") to $(this) when you click on element.
$(".users").attr('class') always return all button element.
$(".users").click(function() {
alert($(this).attr('class').split());
});
When you use a getter method like .attr('class') on a set of elements, it will return the value of the first element in the set. when you say $('.users') it will return all elements with the class users.
In your case you want to have reference to the clicked button, which is available in the click handler as this so you can
$(document).ready(function() {
var users = ['u1', 'u2', 'u3']
for (var i = 0; i < users.length; i++) {
var $currentUser = $('<button class="users ' + i + '"></button>');
$currentUser.text(users[i]);
$currentUser.appendTo('.userList');
}
$(".users").click(function() {
alert(this.className.split(/\s+/));
alert(this.classList); //for ie 10+
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="userList"></div>
The function below allows users to filter products by data-attributes, and accommodates filtering by multiple values simultaneously. It does this by creating an array of the values selected, and when any of the values are clicked (in this case checked/unchecked) it hides all the items and then re-shows those that match the values in the updated array.
It works correctly when filtering for one data-attribute, but when combined to filter by more than one attribute it no longer shows all results matching any of the values and instead only shows results matching all the specified values.
I've posted a fiddle which demonstrates the problem here: http://jsfiddle.net/chayacooper/WZpMh/94/ All but one of the items have the values of both data-style="V-Neck" and data-color="Black" and they should therefore remain visible if either of the filters are selected, but if another value from a different data-attribute some of the items are hidden.
$(document).ready(function () {
var selected = [];
$('#attributes-Colors *').click(function () {
var attrColor = $(this).data('color');
var $this = $(this);
if ($this.parent().hasClass("active")) {
$this.parent().removeClass("active");
selected.splice(selected.indexOf(attrColor),1);
}
else {
$this.parent().addClass("active");
selected.push(attrColor);
}
$("#content").find("*").hide();
$.each(selected, function(index,item) {
$('#content').find('[data-color *="' + item + '"]').show();
});
return false;
});
$('#attributes-Silhouettes *').click(function () {
var attrStyle = $(this).data('style');
var $this = $(this);
if ($this.parent().hasClass("active")) {
$this.parent().removeClass("active");
selected.splice(selected.indexOf(attrStyle),1);
}
else {
$this.parent().addClass("active");
selected.push(attrStyle);
}
$("#content").find("*").hide();
$.each(selected, function(index,item) {
$('#content').find('[data-style *="' + item + '"]').show();
});
return false;
});
});
Both of your handlers are updating the selected array, but only one handler executes on a click. The first one if a color was (de)selected, the second if a style. Let's say you've clicked on "Black" and "Crew Neck". At that time your selected array would look like this: [ "Black", "Crew_Neck" ]. The next time you make a selection, let's say you click "Short Sleeves", the second (style) handler executes. Here's what is happening:
Short_Sleeves gets added to the selected array.
All of the items are hidden using $("#content").find("*").hide();
The selected array is iterated and items are shown again based on a dynamic selector.
Number 3 is the problem. In the above example, a style was clicked so the style handler is executing. Any items in the selected array that are colors will fail because, for example, no elements will be found with a selector such as $('#content').find('[data-style *="Black"]').show();.
I would suggest 2 things.
Keep 2 arrays of selections, one for color, one for style.
Combine your code to use only a single handler for both groups.
Here's a (mostly) working example.
Note that I added a data-type="color|style" to your .filterOptions containers to allow for combining to use a single handler and still know which group was changed.
Here's the full script:
$(document).ready(function () {
// use 2 arrays so the combined handler uses correct group
var selected = { color: [], style: [] };
// code was similar enough to combine to 1 handler for both groups
$('.filterOptions').on("click", "a", function (e) {
// figure out which group...
var type = $(e.delegateTarget).data("type");
var $this = $(this);
// ...and the value of the checkbox checked
var attrValue = $this.data(type);
// same as before but using 'type' to access the correct array
if ($this.parent().hasClass("active")) {
$this.parent().removeClass("active");
selected[type].splice(selected[type].indexOf(attrValue),1);
}
else {
$this.parent().addClass("active");
selected[type].push(attrValue);
}
// also showing all again if no more boxes are checked
if (attrValue == 'All' || $(".active", ".filterOptions").length == 0) {
$('#content').find('*').show();
}
else {
// hide 'em all
$("#content").find("*").hide();
// go through both style and color arrays
for (var key in selected) {
// and show any that have been checked
$.each(selected[key], function(index,item) {
$('#content').find('[data-' + key + ' *="' + item + '"]').show();
});
}
}
});
});
UPDATE: incorporating suggestions from comments
To make the handler work with checkboxes instead of links was a small change to the event binding code. It now uses the change method instead of click and listens for :checkbox elements instead of a:
$('.filterOptions').on("change", ":checkbox", function (e) {
// handler code
});
The "All" options "hiccup" was a little harder to fix than I thought it would be. Here's what I ended up with:
// get a jQuery object with all the options the user selected
var checked = $(":checked", ".filterOptions");
// show all of the available options if...
if (checked.length == 0 // ...no boxes are checked
|| // ...or...
checked.filter(".all").length > 0) // ...at least one "All" box is checked...
{
// remainder of code, including else block, unchanged
}
I also added an all class to the appropriate checkbox elements to simplify the above conditional.
Updated Fiddle