Find duplicate item in unordered list - javascript

I have two unordered lists. One unordered lists get populated dynamically and you double click it's items to add to the other unordered list.
I'm trying to figure out how to detect if the item on the dynamically populated list isn't already in the other list. If it is. then it shouldn't get added again. Don't want to add duplicate items.
The code that populates the ul:
.done(function(html) {
var results = jQuery.parseJSON(html);
$('#store_sel').children().remove();
for (var i = 0; i <= results.length; i++){
$("#store_selection").append("<li class='" + results[i].name + " stores'" + "id= " + results[i].uuid +
+ ">" + results[i].name + "</li>");
}
});
The event:
$('#store_selection').on('dblclick', 'li', function(e) {
e.stopPropagation();
e.preventDefault();
if ($('#store_selected').find($(this))) {
console.log('item already there');
} else {
$('#store_selected').append($(this).clone());
//$(this).remove();
}
});
EDIT: Why isn't this working? It basically goes to console.log even if the ul is empty.

There are several issues within your if statement.
An element can't be in 2 places at the same time. Therefore you can't locate this in 2 lists
An object will always return truthy. Use length of returned selector collection instead
Since ID's must be unique in a page I suggest you use a data- attribute to store the UUID
<li data-uuid="xxxx-yyy">
Then when you search:
var uuid = $(this).data('uuid')
if ($('#store_selected').find('.stores[data-uuid='+uuid+']').length) {
console.log('item already there');
} else {
$('#store_selected').append($(this).clone());
//$(this).remove();
}

Related

Create li list with ids from 'for each' function and then use onclick dynamically on the newly created items

I have a function below that gets me all the available pages (name.displayname) in my embedded report, along with their ids (page.name) and puts them into a html list
$("#showreports").click(async function() {
let pages = await report.getPages();
ul = document.createElement('ul');
pages.forEach(function(page) {
document.getElementById("reportlist").innerHTML += '<li id=' + page.name +'>' + page.displayName + '</li>';
});
});
I have a separate function that can reload the active report page on click using a static ID
(ID is in the format "ReportSectionxxxxxxxxxxxx")
$("#report1").click(function() {
report.setPage("ReportSection80609a506b587e04c723");
//Log.logText("Page was set to: ReportSectiona271643cba2213c935be");
});
$("#report2").click(function() {
report.setPage("ReportSection6fc0357887900a6c4345");
//Log.logText("Page was set to: ReportSectiona271643cba2213c935be");
})
Ideally what I want is to be able to use the newly created list to act as a navigation for updating the active page. Where I can click on one of the pages in the list and it will use report.setPage
Maybe something like:
$("#reportlist").click(function() {
report.setPage(li);
})
Any help would be appreciated

Need JQuery or JavaScript to use .contains() with && logic

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");
}
});
});

Checkboxes that display results with jQuery

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.

Jquery .next() not working as expected

On my website, I have an unordered list that is originally unpopulated. After the page loads, I use jquery and ajax to call a php page that loads data in from a database. It then returns that data to the ajax call so that it can create a bunch of list items and append them to the unordered list.
Each of these list items can be selected. When they are selected, I append a class to them called "selected-li" which just underlines the text. I also have arrows to navigate between the list items (i.e. up-arrow and down-arrow). I thought that the following would work...
$("#down-arrow").click(function(){
$(".selected-li").next().addClass("selected-li");
});
...and it does, just not as expected. I have to click the down arrow twice in order for the class to be added to the next item. For some reason, it thinks that there is an invisible list item in between each of the ones displayed.
As a fix, I did the following:
$("#down-arrow").click(function(){
$(".selected-li").next().next().addClass("selected-li");
});
It works now, but why does this happen? Why does it think that there is an extra li in there?
HTML code:
<div id="up-down-arrows">
<img id="up-arrow" height="35px" width="35px" src="/../images/circle_arrow_up.png"></img><br><br><br>
<img id="down-arrow" height="35px" width="35px" src="/../images/circle_arrow_down.png"></img>
</div>
<div id="article-list"><ul id="unordered-list"></ul></div>
Javascript code on return from ajax call:
function(data){
for(var i = 0; i < data.length; i++){
title = data[i]["name"];
$("#unordered-list").append('<li title="' + title + '">' + title + '</li><br>');
}
}
It's the <br> tag. The .next() function gets the next sibling element, not the next <li> element. Besides it's not valid html to have a <br> between <li> elements.
Even if you find the reason why you have to call .next() twice, is this really going to do what you want? There could be multiple items with the "selected-li" class, and your code will then add that class to the items that are after each of them. That is, if items 3 & 7 are selected, Items 4 & 8 will also be selected after the down button is clicked. Here's a jsfiddle showing this.
LIVE DEMO
.next() or no .next()... The <ul> tag is not supposed to hold <br> but list tag <li>.
Additionally I would suggest going like:
This will also loop the selected LI element on buttons click.
var $ul = $('#unordered-list');
var liData = ""; // Popoulate string and append only once to UL (see below)
var $li; // Reference to future created LI
var liN; // Will hold the number of LI after creation
var sel = 0; // Selected index
$.ajax({
dataType: "json",
url: 'yourFile.json',
success : function(data){
for(var i = 0; i < data.length; i++){
title = data[i].name;
liData += '<li title="' + title + '">' + title + '</li>'; // <br>? c'mon..
}
$ul.append( liData ); // Append only once (performance matters!)
$li = $('li', $ul); // Find appended LI - put in collection
liN = $li.length; // How many?
$li.eq(sel).addClass('selected-li'); // (add class to first)
}
});
$('#up-arrow, #down-arrow').click(function(){
sel = (this.id.match('down') ? ++sel : --sel) % liN ;
$li.removeClass('selected-li').eq(sel).addClass('selected-li');
});

Best way to insert an array of jQuery elements to the page

What's the best way of appending an array of jQuery elements to the page?
I know that if I'm appending straight HTML tags then they should be collected together in an array then the array appended. This is the fastest method. However I have a collection of jQuery elements that have events created on them so I can't just concatenate strings together.
Example:
function displayCommentActions(actions) {
var html = [];
module.$element.find('.comments .actions li').remove();
$.each(actions, function(key, value) {
html.push($('<li class="' + key + '">' + value.name + '</li>').bind({
click: function(event) {
alert('found click');
}
}));
});
$.each(html, function(count, item) {
module.$element.find('.comments .actions').append(item);
})
};
This is ugly because it takes two loops. One to create the jQuery objects and one to output them to the page. How can I improve my code?
#efritz, doing .remove() on every list item is slow and expensive, a quick wipe of list items would be .empty().
#Samuel you can't avoid looping here. .append() takes either a string (of html) or a jquery object. It can't take an array of jquery objects.
var actionsList = module.$element.find('.comments .actions');
actionsList.empty() // Remove all contents of our <UL>
$.each(actions, function(class, value) {
var listItem = $('<li />').addClass(class) // Create our <li> and add our class
.append('' + value.name + '') // Add our link
.click(function() { // Click bind event
alert('Clicked item');
});
actionsList.append(listItem); // Add our <li> to our <ul>
}
This is probably as small as you're gonna get it Samuel, it's an improvement from your dual-loop at least.
Would something like this not work?
function displayCommentActions(actions) {
var target = module.$element.find('.comments .actions li')
target.remove();
$.each(actions, function(k, v) {
var item = $('<li />').addClass(key);
var link = $('<a />')
.attr('href', 'javascript:void(0);')
.html(value.name)
.click(function() {
alert('found click');
});
elem.append(item);
target.append(item);
}
}
Also, remember that repeated selectors can be expensive - store them if at all possible.

Categories