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>.
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.
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>
I have a list of <li> tags inside a <ul> , I have a javascript function that add 'active' class to one of them when clicked and remove it from its siblings , I want to implement this to all except the last <li>
<ul class='tabs'>
<li>Home</li>
<li>About</li>
<li>Contact</li>
<li>
<form>
<input type='text'/>
<button></button>
</form>
</li>
</ul>
The JS function:
[].forEach.call(document.querySelectorAll('.tabs li'), function(ele) {
ele.addEventListener('click', function(e) {
if(ele.nextSibling){
document.querySelector(".tabs li.active").classList.remove("active");
ele.classList.add("active");
}
});
});
You can use the slice method.
const listItems = Array.from(document.querySelectorAll('.tabs li'));
const selectableListItems = listItems.slice(0, -1);
selectableListItems.forEach(function(ele) {
ele.addEventListener('click', function(e) {
if(ele.nextSibling){
document.querySelector(".tabs li.active").classList.remove("active");
ele.classList.add("active");
}
});
});
Or adjust your query selector using a pseudo-class in order to select all items except the last one:
document.querySelectorAll('.tabs li:not(:last-child)');
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 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'));
});