how to remove duplicate li in div using js?
<div id="tags">
<li id="tag">sport</li>
<li id="tag">news</li>
<li id="tag">sport</li>
<li id="tag">sport</li>
<li id="tag">cars</li>
</div>
must become:
sport
news
cars
You can do that in following steps:
Select all the elements and create Set containing all the texts of <li>
Then loop through elements list using forEach
Check if the Set doesn't contain the innerHTML of current element then remove the element
If set contains the text then don't remove the element but remove the text from Set
Note: id of element should be unique in the whole document. Two elements can't have same id
const tags = [...document.querySelectorAll('#tags > li')];
const texts = new Set(tags.map(x => x.innerHTML));
tags.forEach(tag => {
if(texts.has(tag.innerHTML)){
texts.delete(tag.innerHTML);
}
else{
tag.remove()
}
})
<div id="tags">
<li>sport</li>
<li>news</li>
<li>sport</li>
<li>sport</li>
<li>cars</li>
</div>
you can just iterate over the selected node list without much overhead using one loop, like this:
let elements = document.querySelectorAll("li");
textArr = [];
elements.forEach(function(d, i) {
if(textArr.indexOf(d.innerText) > -1) {
d.remove();
}
else {
textArr.push(d.innerText);
}
});
<div id="tags">
<li id="tag">sport</li>
<li id="tag">news</li>
<li id="tag">sport</li>
<li id="tag">sport</li>
<li id="tag">cars</li>
</div>
Related
On every change of input, i need to remove all <li> elements where inner text don't match the input value. The problem is: it don't remove all lines that doesn't match at once.
My code:
<input name="tag-input" id="tag-input" type='text'>
<div id="list-pol">
<ul id="list-pol-select">
<li class="list-pol-item">Fish</li>
<li class="list-pol-item">Dog</li>
<li class="list-pol-item">Chameleon</li>
<li class="list-pol-item">Cat</li>
</ul>
</div>
<script type="text/javascript">
var input = document.getElementById('tag-input');
function updateList(){
if(document.getElementsByClassName("list-pol-item")[0]){
var list = document.getElementsByClassName("list-pol-item");
for (var i = 0; i < list.length; i++) {
var tag = list.item(i).innerText;
if(input.value !== tag.substring(0,input.value.length)){
list.item(i).remove();
}
}
}
}
input.addEventListener('input', updateList);
</script>
getElementsByClassName will give you a live collection, which is very confusing. If the ith element in the collection loses the class name, the collection will lose that element and shift down immediately. If that happens while you're trying to iterate over it - like here - you'll be in trouble.
const foos = document.getElementsByClassName('foo');
foos[0].remove();
console.log(foos.length);
console.log(foos[0]);
<div class="foo">1</div>
<div class="foo">2</div>
Turn it into a (non-live) array instead first - or use querySelectorAll, which returns a static NodeList.
var input = document.getElementById('tag-input');
function updateList() {
for (const item of document.querySelectorAll('.list-pol-item')) {
if (!item.textContent.startsWith(input.value)) {
item.remove();
}
}
}
input.addEventListener('input', updateList);
<input name="tag-input" id="tag-input" type='text'>
<div id="list-pol">
<ul id="list-pol-select">
<li class="list-pol-item">Fish</li>
<li class="list-pol-item">Dog</li>
<li class="list-pol-item">Chameleon</li>
<li class="list-pol-item">Cat</li>
</ul>
</div>
If you paste in "Fish", you'll see that Fish is the only item that remains.
But your current logic is weird - do you really want to .remove() the items that don't match? Unless someone pastes in text that matches exactly, everything will be removed. Did you want to toggle the items' display instead?
var input = document.getElementById('tag-input');
function updateList() {
for (const item of document.querySelectorAll('.list-pol-item')) {
item.classList.toggle('hidden', !item.textContent.startsWith(input.value));
}
}
input.addEventListener('input', updateList);
.hidden {
display: none;
}
<input name="tag-input" id="tag-input" type='text'>
<div id="list-pol">
<ul id="list-pol-select">
<li class="list-pol-item">Fish</li>
<li class="list-pol-item">Dog</li>
<li class="list-pol-item">Chameleon</li>
<li class="list-pol-item">Cat</li>
</ul>
</div>
You also might consider comparing the lower-cased input against the lower-cased list item, instead of requiring a case match for the elements to display.
So i got this list
<ul>
<li> This is</li>
<li> a very nice</li>
<li> list</li>
</ul>
This is
a very nice
list
and this code to put the content of the array into the list
var nevek =["tom", "bob","kate"];
nevek.forEach(element => {
$('ul').append( " <li> element </li>");
But instead of adding the content from the array, it just adds 3 new listitems like this
This is
a very nice
list
element
element
element
How should I refer to the individual element in the array to print them out?
You should append the element at each iteration instead of the string, this can be done using template literals as follows:
var nevek =["tom", "bob","kate"];
nevek.forEach(element => {
$('ul').append( ` <li> ${element} </li>`);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li> This is</li>
<li> a very nice</li>
<li> list</li>
</ul>
I'm creating a web application that returns an NBA player's name and jersey number when a user searches for a player (and the match is found).
I can return the name that has been searched, but I cannot return the jersey number.
I've tried setting style.display = true, which works for the name node, but I can't get this to work on the jersey node.
Here's how my HTML has been created with DOM manipulation from a JSON:
function searchPlayer() {
let input, filter, ul, li, playerName, i;
input = document.getElementById("search-bar");
filter = input.value.toUpperCase();
li = document.getElementById("player-list").getElementsByTagName("li");
Object.keys(li).forEach(function(name) {
playerName = li[name].innerHTML;
if (playerName.toUpperCase().indexOf(filter) > -1) {
li[name].style.display = true;
} else {
li[name].style.display = "none";
}
})
}
<div id="player-list-section">
<ul id="player-list">
<li id="player">
<li id="full-name">Alex Abrines</li>
<li id="jersey">Jersey: 8</li>
</li>
<li id="player">
<li id="full-name">Quincy Acy</li>
<li id="jersey">Jersey: 13</li>
</li>
</ul>
</div>
<input id="search-bar" />
<button onclick="searchPlayer()">Search</button>
I know I can access the child node of player using e.g. li[name].childNode[1] (which returns the jersey li), but I can't call anything on this, such as .innerHTML or .style.display.
How can I return both the name and the jersey?
You need to use list-item instead of true if you want to show the list items after hidding them using display:none and use .closest(".player") to toggle the display of the parent instead:
if (playerName.toUpperCase().indexOf(filter) > -1) {
li[name].closest(".player").display = 'block';
} else {
li[name].closest(".player").display = "none";
}
NOTE 1: You need to validate your structure li can't be a parent of another li, check my updated HTML format.
NOTE 2: You need also to replace the duplicate id by common classes since the identifier must be unique in the same document.
function searchPlayer() {
let input, filter, ul, li, playerName, i;
input = document.getElementById("search-bar");
filter = input.value.toUpperCase();
li = document.querySelectorAll("#player-list .full-name");
Object.keys(li).forEach(function(name) {
playerName = li[name].innerHTML;
if (playerName.toUpperCase().indexOf(filter) > -1) {
li[name].closest(".player").style.display = 'list-item';
} else {
li[name].closest(".player").style.display = "none";
}
})
}
<input id="search-bar" oninput='searchPlayer()'>
<div id="player-list-section">
<ul id="player-list">
<li class="player">
<ul>
<li class="full-name">Alex Abrines</li>
<li class="jersey">Jersey: 8</li>
</ul>
</li>
<li class="player">
<ul>
<li class="full-name">Quincy Acy</li>
<li class="jersey">Jersey: 13</li>
</ul>
</li>
</ul>
</div>
I have done coding the first part HTML then JavaScript/JQuery. Now I want to surround the final common list with a UL need to be done using JavaScript/JQuery. So the final common list will be surrounded by two UL instead of one. Eg
Final Outcome
<ul id="CommonLister">
<ul> <!--Need to add this-->
<li class="columnItem">John</li>
<li class="columnItem">Mark</li>
</ul><!--Need to add this-->
</ul>
Current Code
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<ul id="listOne">
<li class="columnItem">John</li><!--will be removed and put under CommonLister-->
<li class="columnItem">James</li>
<li class="columnItem">Mary</li><!--will be removed and put under CommonLister-->
</ul>
<ul id="listTwo">
<li class="columnItem">John</li><!--will be removed and put under CommonLister-->
<li class="columnItem">Mark</li>
<li class="columnItem">Mary</li><!--will be removed and put under CommonLister-->
</ul>
<ul id="CommonLister">
<li class="columnItem">John</li>
<li class="columnItem">Mark</li>
</ul>
</div>
$(function() {
$('#run-code').on('click', function(e) {
e.preventDefault();
//What were you doing? nope.
var currentItems = {}; //Blank object
var $mergeColumn = $('#CommonLister'); //Common list reference
$('.columnItem').each(function(i, el) {
var $el = $(el); //Notation I use to differentiate between the regular HTML Element and jQuery element
if (!currentItems.hasOwnProperty($el.html())) {
//Has this name come up before? if not, create it.
currentItems[$el.html()] = []; //Make it equal to a brand spanking new array
}
currentItems[$el.html()].push(el);
//Add the item to the array
});
$.each(currentItems, function(name, data) {
//Loop through each name. We don't actually use the name variable because we don't care what someone's name is
if (data.length > 1) {
//Do we have more than 1 element in our array? time to move some stuff
$.each(data, function(i, el) {
var $el = $(el); //See note above
if (i == 0) {
//If this is the first element, let's just go ahead and move it to the merge column ul
$el.appendTo($mergeColumn);
} else {
$el.remove(); //Otherwise, we've already got this element so delete this one.
} //end if/else
}); //end $.each(data)
} //end if data.length >1
}); //end $.each(currentItems)
}); //end $.on()
}); //end $(
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="run-code" class="btn btn-success">Click Me</button>
<h4>List 1</h4>
<ul id="listOne">
<li class="columnItem">John</li>
<!--will be removed and put under CommonLister-->
<li class="columnItem">James</li>
<li class="columnItem">Mary</li>
<!--will be removed and put under CommonLister-->
</ul>
<h4>List 2</h4>
<ul id="listTwo">
<li class="columnItem">John</li>
<!--will be removed and put under CommonLister-->
<li class="columnItem">Mark</li>
<li class="columnItem">Mary</li>
<!--will be removed and put under CommonLister-->
</ul>
<h4>Common List</h4>
<ul id="CommonLister">
<!--Extra ul will be added here-->
</ul>
It's invalid nesting a ul directly in a ul like this but if you have to, you could use jquery wrapAll:
$( "li" ).wrapAll( "<ul></ul>" );
See fiddle: https://jsfiddle.net/9xLt6d9f/
I agree with charlietfl that it seems strange to do it this way. However, to answer your question, the best way to force this improperly formatted HTML code would be hardcode it into your original file. Try the following code for the end of your file:
<h4>Common List</h4>
<ul id="CommonLister">
<ul id="CommonListerSub">
<!--Extra ul will be added here-->
</ul>
</ul>
Then, simply change one line of your code:
var $mergeColumn = $('#CommonListerSub'); //Common list reference
This will force it to list the list items under the nested ul tags.
I hope this is an acceptable solution. If for some reason it doesn't work, please comment as to what additional limitations you have, and perhaps share the link of the page that is giving you the required template or format specifications.
I need to write a function in pure JavaScript witn no framework to get all specific tags, but only from first level under parent.
For example: I need to call some function on first <ul> and get all <li> from first level of it (<li> with text 1.2 and <li> with text 2.1)
<div id="sideNavigation">
<ul>
<li class=" act open ">
1.2
<ul>
<li class=" ">
1.2
<ul>
<li class=" ">
1.3
<ul>
<li class=" ">1.4</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li class=" ">
2.1
<ul>
<li class=" ">2.2.1</li>
<li class=" ">2.2.2</li>
<li class=" ">2.2.3</li>
</ul>
</li>
</ul>
</div>
I've been trying to do it like this:
var allLi = document.getElementById("sideNavigation").getElementsByTagName("li");
but it returns all <li> in this div not only first level <li>. Do you have any quick method to solve my problem or do I have to implement a new function to detect depth of nodes
You can use the attribute .children to get those "li"
var firstDepthLi = document.getElementById("sideNavigation").children[0].children;
If you want a generic function you can create something like:
var getElementsByDepth = function(el, tagname, depth) {
var children = el.children;
var res = new Array();
for(var i=0; i<children.length; i++) {
if (children[i].tagName == tagname) {
res.push(children[i]);
if (depth > 0)
res.concat(getElementsByDepth(children[i], tagname, depth-1));
}
}
return res;
}
Try:
var allLi = document.getElementById("sideNavigation").getElementsByTagName("li")[0];
That should return the first li element out of all li's on the page. Change the zero at the end to a different number to get a different element. You could even set a variable for the value:
var liNum = 0;
var allLi = document.getElementById("sideNavigation").getElementsByTagName("li")[liNum];
And in a function:
function getLi(depth) {
var specificLi = document.getElementById("sideNavigation").getElementsByTagName("li")[depth];
return specificLi;
}
var firstLi = getLi(0);
console.log(firstLi);
<div id="sideNavigation">
<ul>
<li>First list tag</li>
<li>Second list tag</li>
<li>Third list tag</li>
</ul>
</div>
And to make the function even shorter, you could just do:
function getLi(depth) {
return document.getElementById("sideNavigation").getElementsByTagName("li")[depth];
}
That should work. :)