Javascript remove multiple html list elements - javascript

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.

Related

remove duplicate elements through javascript

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>

How to display all nested list elements according to input text search?

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>

How do i add an html list item to an already created list in javascript?

I have read other questions and articles on how to add a list item but none of these work. Here is my JavaScript:
var inpu = document.getElementById("inp").value;//get input text
var ul = document.getElementById("lis");//get list
var li = document.getElementById("li");//get list item
li.appendChild(document.createTextNode(inpu));
ul.appendChild(li);
This is what i read that i was supposed to do but they never showed me html so i can see how to do that part, which is why i am assuming that the problem is in my html. This is what my html looks like:
<body>
<input id="inp"/>
<button id="button">Submit</button>
<ul id="lis">
<li id="li"> </li>
</ul>
<script src="This.js"></script>
Any help is greatly appreciated.
If you want to add a new list item, you need to use document.createElement('li'). Your code just replaces the contents of the existing li; the appendChild doesn't do anything, because it's already the last child.
document.getElementById("button").addEventListener("click", function() {
var inpu = document.getElementById("inp").value; //get input text
var ul = document.getElementById("lis"); //get list
var li = document.createElement("li"); //get new list item
li.appendChild(document.createTextNode(inpu));
ul.appendChild(li);
});
<input id="inp" />
<button id="button">Submit</button>
<ul id="lis">
<li id="li"></li>
</ul>
'inpu' is equal to the value in the input field. When you first load that page there is no value in the input field so nothing is getting appended to the list item.
If you put the code into a function and then call that function when you click the button it will work. like this:
html:
<body>
<input id="inp"/>
<button id="button" onclick="putInList()">Submit</button>
<ul id="lis">
<li id="li"> </li>
</ul>
javascript:
var putInList = function() {
var inpu = document.getElementById("inp").value;//get input text
var ul = document.getElementById("lis");//get list
var li = document.getElementById("li");//get list item
li.appendChild(document.createTextNode(inpu));
ul.appendChild(li);
}

inserting li element after a selected li by text

I have the following list:
<ol id="mainlist">
<li>Item1
<ol>
<li>SubItem1</li>
<li>SubItem2</li>
<li>SubItem3</li>
</ol>
</li>
<li>Item2
<ol>
<li>SubItem1</li>
<li>SubItem2</li>
<li>SubItem3</li>
</ol>
</li>
<li>Item3
<ol>
<li>SubItem1</li>
<li>SubItem2</li>
<li>SubItem3</li>
</ol>
</li>
</ol>
<span>Add text</span><input type="text" /><span>, after the element </span> <input type="text" /><input type="button" value="Add" />
I have two text inputs. The first one is where the user is supposed to write the text that he/she wants to insert after the element specified in the second text input box.
The problem is that I don't know how to select the correct li based on its text.
I've tried the following using jQuery:
$('input[type="button"]').on('click',function(){
$item=$('<li>',{
html:$('input')[0].value
});
$position=$('input')[1];
$('li:contains("'+$position.value+'")').filter(function() {
return $(this).text() == $position.value;
}).after($item);
});
But that doesn't work. I don't know how to select the item where I am going to insert the object <li>.
I've cleaned up your code and had to make a slight amendment to make it work properly.
One problem you had was that your structure was <ol><li><ol><li> which meant that any searching for li elements or even ol li elements meant it appends to both the parent and child which doesn't seem to be what you want to achieve.
Hope this is what you were trying to achieve.
$(document).ready(function() {
$('input[type="button"]').on('click', function() {
var item = $('<li>', {
html: $('input')[0].value
});
var position = $('input')[1].value;
$('ol li').each(function() {
var firstWord = $(this).text().substr(0, $(this).text().indexOf(" "));
if (firstWord.trim() == position) {
$(this).after(item);
}
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ol id="mainlist">
<li>Item1
<ol>
<li>SubItem1</li>
<li>SubItem2</li>
<li>SubItem3</li>
</ol>
</li>
<li>Item2
<ol>
<li>SubItem1</li>
<li>SubItem2</li>
<li>SubItem3</li>
</ol>
</li>
<li>Item3
<ol>
<li>SubItem1</li>
<li>SubItem2</li>
<li>SubItem3</li>
</ol>
</li>
</ol>
<span>Add text</span>
<input type="text" /><span>, after the element </span>
<input type="text" />
<input type="button" value="Add" />
The main problem is that you used .value to get the input value in jQuery. However, .value belongs to javascript. Since you are using jquery, you need to use .val().
I worked on an example: https://jsfiddle.net/6r18ag68/2/
$('input[type="button"]').on('click',function(){
var newitem = $('<li>' + $('#txt1').val() + '</li>');
$('li').filter(function () {
return $(this).text() == $('#txt2').val();
}).after(newitem);
$('#mainlist > li').filter(
function(){
return $.trim($(this).html().split('<')[0]) == $('#txt2').val(); }).append(newitem);
});
});
A few points:
You should declare variables such as $item with var, unless you want them to be put on an enclosing scope
The statement, return $(this).text() == $posicion.value;, has "position" misspelled
Some li nodes are identical in your example. I assume you need a way to differentiate them by ancestor li element. That means you need three inputs
How about this?
$('input[type="button"]').on('click',function(){
var newText = $('input#newText').val();
var ancestorLiText = $('input#ancestorLiText').val();
var liText = $('input#liText').val();
var $ancestorEl = $('ol#mainlist').find('li').filter(function (i) {
return $(this).text() === ancestorLiText;
});
var $el = $ancestorEl.find('li').filter(function (i) {
return $(this).text() === liText;
});
var $newLi = $('<li>').text(newText);
$el.after($newLi);
});
You should be able to derive what HTML you need from this code (you'll need inputs with IDs "newText", "ancestorLiText", and "liText")
Also, note that the :contains() selector is like a global search on an element's text. Therefore a search for li:contains(chair) would match both <li>chair</li> and <li>not a chair</li>.

How to map every element and its children

I am trying to get every element and alls its children and there children and add them to a list. But at the moment its only showing the first two children.
Is there a better way for doing this?
here is jsfiddle, http://jsfiddle.net/7mNsH/2/
here is what i have so far
$('#main').children().each(function(){
var classs = $(this).attr('class');
$('#hell ul').append('<li class="'+classs+'">'+classs+'</li>');
});
<div id="main">
<div class="inner">
<div class="innerinner"></div>
</div>
<div class="inner2">
<div class="inner2inner2">
<div class="inner2inner2inner2"></div>
</div>
</div>
</div>
so at the moment its currently showing this
<ul>
<li class="inner">inner</li>
<li class="inner2">inner2</li>
</ul>
but i need it to look like this
<ul>
<li class="inner">inner
<ul>
<li class="innerinner">innerinner</li>
</ul>
</li>
<li class="inner2">inner2
<ul>
<li class="innerinner">innerinner
<ul>
<li class="inner2inner2inner2">inner2inner2inner2</li>
</ul>
</li>
</ul>
</li>
</ul>
You seem to be looking for a recursive solution. If you look at your current code, what you are accomplishing is iterating over #main's children, appending them to a ul, and then finishing. You need to be sure to keep checking each child for their own children (and all of their children!) in order to get the fully recursive list of children.
Check out working JSFiddle here: http://jsfiddle.net/uK6kG/5/
function listChildren(element, container) {
//Get the current node's class
var parentClass = element.attr('class');
//Go ahead and construct a list element
var parentContainer = $('<li class="' + parentClass + '">' + parentClass + '</li>');
//Now iterate over the current node's children (if there are any)
if (element.children().length > 0) {
var childList = $('<ul></ul>');
element.children().each(function () {
//Recursively call the list children function!
listChildren($(this), childList);
});
//Actually add the children to the current node
parentContainer.append(childList);
}
//Add us to our own container
container.append(parentContainer);
}
$('#main').children().each(function () {
listChildren($(this), $('#list-container #top-ul'));
});

Categories

Resources