I've got a list of items and I need to create a function that puts a number front of each item in the list. So "pine nuts", would become "2. pine nuts"
Here's the list I'm passing in (with the parameter ulPassed) -
<li id="one" class="hot"><em>fresh</em> figs</li>
<li id="two" class="hot">pine nuts</li>
<li id="three" class="hot">honey</li>
<li id="four" class="cold">balsamic vinegar</li>
<li id="five" class="cold">yoghurt coated banana chips</li>
Here's my code so far -
function swapInnerHTML(ulPassed){
ulPassed = ulPassed.innerHTML;
var grocList = document.getElementById('groceries');
var listItems = grocList.getElementsByTagName('li');
var listNumber = 1;
var count = 0;
for (count = 0; count < ulPassed.length; count++) {
var thisItem = listItems[count].getAttribute('class'); //pointing towards each class item
var foodText = thisItem.firstChild.nodeValue; //pointing towards the child node of each item
foodText = foodText.replace(foodText, listNumber+". "+foodText); //replace the text
thisItem.firstChild.nodeValue = foodText; //update the text
listNumber++; //next list number
}
console.log(ulPassed);
return ulPassed;
}
So I'm attempting a for loop to cycle through the list, and replace each of the food items with itself but with a list number in front of them, also a ". ". In that same loop I'm increasing the list number. The problem I'm having is getting it to replace the text itself, I feel as if I'm pretty close, but I don't feel as if I'm using the firstChild.nodeValue correctly.
Any help would be great. Thanks!
Without using Javascript you could use an ordered list <ol> instead of a unordered list <ul> especially if this is done only for a visual purpose. Also, counters in CSS can be easily styled, e.g.
ul {
counter-reset: food 0;
}
li::before {
counter-increment: food;
content: counter(food) ". ";
}
<ul>
<li class="hot"><em>fresh</em> figs</li>
<li class="hot">pine nuts</li>
<li class="hot">honey</li>
<li class="cold">balsamic vinegar</li>
<li class="cold">yoghurt coated banana chips</li>
</ul>
Otherwise in Javascript you could loop over all the list-items and set their innerHTML property using the index inside the map function
[...document.querySelectorAll('li')].map((food, i) =>
food.innerHTML = [++i, food.innerHTML].join('. ')
);
<ul>
<li class="hot"><em>fresh</em> figs</li>
<li class="hot">pine nuts</li>
<li class="hot">honey</li>
<li class="cold">balsamic vinegar</li>
<li class="cold">yoghurt coated banana chips</li>
</ul>
The suggestion with the use of the map is great, my suggestion is for the more beginners:
const list = document.getElementsByTagName('li');
let arrLi = [].slice.call(list);
for (const [i, v] of arrLi.entries()) {
v.innerHTML = `${i+1} ${v.innerHTML}`
}
Related
I made an example of a sandbox with an example inside there is a list with data-attribute and menu items are scattered and I want to sort these items but I did not succeed this, help to understand
https://codepen.io/topicstarter/pen/gOYMedv
var mass = [];
var children = document.querySelector(".menu").children;
for(var i = 0; i < children.length; i++){
mass.push(children[i].getAttribute('data-num'));
}
mass.sort(function (a, b) {
return a - b;
});
console.log(mass);
<ul class="menu">
<li data-num="1">a</li>
<li data-num="3">c</li>
<li data-num="5">e</li>
<li data-num="2">b</li>
<li data-num="4">d</li>
<li data-num="6">f</li>
</ul>
You should do it like this:
// target to menu
const $menu = document.querySelector(".menu");
// menu items (ES6 feature: spread operator)
const $menuItems = [...$menu.children];
// sort by data-num
const $menuItemsSort = $menuItems.sort((a, b) => a.dataset.num - b.dataset.num);
// append items sort to menu
$menuItemsSort.forEach(element => {
$menu.appendChild(element);
});
<ul class="menu">
<li data-num="1">a</li>
<li data-num="3">c</li>
<li data-num="5">e</li>
<li data-num="2">b</li>
<li data-num="4">d</li>
<li data-num="6">f</li>
</ul>
It happened to me, but they helped me.
[...document.querySelector('.menu').children]
.sort((a, b) => a.dataset.num - b.dataset.num)
.forEach(n => n.parentNode.appendChild(n));
<ul class="menu">
<li data-num="1">a</li>
<li data-num="3">c</li>
<li data-num="5">e</li>
<li data-num="2">b</li>
<li data-num="4">d</li>
<li data-num="6">f</li>
</ul>
I am dynamically generating li elements.
some of li element is having empty div.
like
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
i don't want to show 3rd li element bullet on ui, but want to show other two li bullets.
is there any way to fix this?
Thanks
Amit
You've said you're creating the elements. If so, then just leave off that li or apply a class to it when the div is empty that sets list-style: none.
If you're doing this after the fact, then without changing the structure, adding classes, etc., I can't think of a CSS solution that works based on the div being empty (rather than it always being the third item, which I'm sure isn't the question).
The JavaScript solution is simple, but I don't like using JavaScript for this.
const items = document.querySelectorAll(".mybuttons li");
for (const item of items) {
if (!item.textContent.trim()) {
item.style.listStyle = "none";
}
}
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
Or with ES5 only:
var items = document.querySelectorAll(".mybuttons li");
for (var n = 0; n < items.length; ++n) {
if (!items[n].textContent.trim()) {
items[n].style.listStyle = "none";
}
}
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
I shouldn't have used style.listStyle above, I should have done what I said in the first paragraph above and used a class:
const items = document.querySelectorAll(".mybuttons li");
for (const item of items) {
if (!item.textContent.trim()) {
item.classList.add("empty");
}
}
.empty {
list-style: none;
}
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
Use querySelectorAll to select all the li elements and inside a forEach loop if the text in a div is empty remove the list style from it.
var a=document.querySelectorAll('li');
a.forEach((e)=>{
if(e.childNodes[0].textContent=="")
e.style.listStyle='none';
})
<ul class="mybuttons" >
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
<li class="mybutton"><div>erere</div></li>
<li class="mybutton"><div></div></li>
<li class="mybutton"><div>rere</div></li>
</ul>
With JS:
{
const init = () => {
const btns = document.querySelectorAll(`.mybutton`);
const allBtns = Array.from(btns);
btns.forEach(btn => {
if(btn.textContent === ''){
btn.style.listStyle = 'none';
}
}
}
init();
}
This might work
just check the text in the li element and eventually remove it:
let items = document.getElementsByTagName("li");
for (var i = 0; i < items.length; ++i) {
let text = items[i].innerText;
if(!text) items[i].remove();
}
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton"><div></div></li>
</ul>
Yes. Use the list-style property and assign none to it.
<ul class="mybuttons">
<li class="mybutton"><div>kkk</div></li>
<li class="mybutton"><div>llll</div></li>
<li class="mybutton" style="list-style: none;"><div>HHH</div></li>
</ul>
I watched a youtube video about coding in vanilla javascript because I'm currently studying javascript. I wanted to add an "adder" for names that start with letter a.
I wrote a do1 function and I added div between all names that start with a. I don't know what's going on here to be host what's the problem. I'm currently moving from basics to intermediate level in javascript, I'm trying to practice my javascript skills in any possible way. function filter names was written by someone else. I don't have that much skills to do something like that.
So if you have any ideas on how should I practice js. If you have any websites or even tasks that could help me in learning javascript. would appreciate if you linked me any in comments section.
let filterInput = document.getElementById('filterInput');
filterInput.addEventListener('keyup', filterNames);
function filterNames() {
// Get value of input
let filterValue = document.getElementById('filterInput').value.toUpperCase();
// Get names ul
let ul = document.getElementById('names');
// Get lis from ul
let li = ul.querySelectorAll('li.collection-item');
// Loop through collection-item lis
for (let i = 0; i < li.length; i++) {
let a = li[i].getElementsByTagName('a')[0];
// If matched
if (a.innerHTML.toUpperCase().indexOf(filterValue) > -1) {
li[i].style.display = '';
} else {
li[i].style.display = 'none';
}
}
}
function do1() {
var input1 = document.getElementById('ipt1').value;
var item = document.createTextNode(input1);
var li = document.createElement('li').className = "collection-header";
var a = document.createElement('a');
var child1 = li.appendChild(a);
var div = document.getElementById('div1');
div1.appendChild(item).innerHTML = item;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.99.0/css/materialize.css">
<div class="container">
<h1 class="center-align">
My Contacts
</h1>
<input type="text" id="filterInput" placeholder="Search names...">
<ul id="names" class="collection with-header">
<li class="collection-header">
<h5>A</h5> <input type="box" id="ipt1"> <button onclick="do1();">`click me to add another contact`</button>
</li>
<div id="div1">
<li class="collection-item">
Abe
</li>
<li class="collection-item">
Adam
</li>
<li class="collection-item">
Alan
</li>
<li class="collection-item">
Anna
</li>
</div>
<li class="collection-header">
<h5>B</h5> <input type="box" id="ipt2">
</li>
<li class="collection-item">
Beth
</li>
<li class="collection-item">
Bill
</li>
<li class="collection-item">
Bob
</li>
<li class="collection-item">
Brad
</li>
<li class="collection-header">
<h5>C</h5> <input type="box" id="ipt3">
</li>
<li class="collection-item">
Carrie
</li>
<li class="collection-item">
Cathy
</li>
<li class="collection-item">
Courtney
</li>
</ul>
</div>
Here is a working example:
let filterInput = document.getElementById("filterInput");
filterInput.addEventListener("keyup", filterNames);
function filterNames() {
// Get value of input
let filterValue = document.getElementById("filterInput").value.toUpperCase();
// Get names ul
let ul = document.getElementById("names");
// Get lis from ul
let li = ul.querySelectorAll("li.collection-item");
// Loop through collection-item lis
for (let i = 0; i < li.length; i++) {
let a = li[i].getElementsByTagName("a")[0];
// If matched
if (a.innerHTML.toUpperCase().indexOf(filterValue) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
function do1() {
var input1 = document.getElementById("ipt1").value;
var a = document.createElement("a");
a.textContent = input1;
a.setAttribute('href', "#");
var li = (document.createElement("li"));
li.setAttribute('class', 'collection-item');
li.appendChild(a);
var div = document.getElementById("div1");
div1.appendChild(li);
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.99.0/css/materialize.css">
<div class="container">
<h1 class="center-align">
My Contacts
</h1>
<input type="text" id="filterInput" placeholder="Search names...">
<ul id="names" class="collection with-header">
<li class="collection-header">
<h5>A</h5> <input type="box" id="ipt1"> <button onclick="do1();">`click me to add another contact`</button>
</li>
<div id="div1">
<li class="collection-item">
Abe
</li>
<li class="collection-item">
Adam
</li>
<li class="collection-item">
Alan
</li>
<li class="collection-item">
Anna
</li>
</div>
<li class="collection-header">
<h5>B</h5> <input type="box" id="ipt2">
</li>
<li class="collection-item">
Beth
</li>
<li class="collection-item">
Bill
</li>
<li class="collection-item">
Bob
</li>
<li class="collection-item">
Brad
</li>
<li class="collection-header">
<h5>C</h5> <input type="box" id="ipt3">
</li>
<li class="collection-item">
Carrie
</li>
<li class="collection-item">
Cathy
</li>
<li class="collection-item">
Courtney
</li>
</ul>
</div>
The issues you were having
var item = document.createTextNode(input1); // this line was not needed as you already had the text
var li = document.createElement('li').className = "collection-header"; // assigning a string here causing errors. Use setAttribute() not class name.
div1.appendChild(item).innerHTML = item; assigning to innerHTML of appended child
so changing the function from:
function do1() {
var input1 = document.getElementById('ipt1').value;
var item = document.createTextNode(input1);
var li = document.createElement('li').className = "collection-header";
var a = document.createElement('a');
var child1 = li.appendChild(a);
var div = document.getElementById('div1');
div1.appendChild(item).innerHTML = item;
}
to :
function do1() {
var input1 = document.getElementById("ipt1").value; // get the value of the input
var a = document.createElement("a"); // create new a tag
a.textContent = input1; // Set the text of the a tag
a.setAttribute('href', "#"); // Set the href attribute of the a tag
var li = (document.createElement("li")); // Create new list item
li.setAttribute('class', 'collection-item'); // Set class attribute of li tag
li.appendChild(a); // Append the a tag to the list item
var div = document.getElementById("div1"); // get the containing div
div1.appendChild(li); // append the new list item to the div
}
Now you are able to append to the first div around A. To append to others, you first would need div tags with different ID's. Then you would just need to either create a new function for each one or pass which div ID you were calling the method on..
Hope this helps.
Look at the following line of code:
var li = document.createElement('li').className = "collection-header";
This code assigns the string "collection-header" to the variable named li. Strings don't have the appendChild function, so your code is crashing on the line:
var child1 = li.appendChild(a);
Not sure what your intention is by using two = in the same line of code. If you can explain that further, perhaps someone can help you achieve your desired result.
Is there a way to get the content of an ordered list item's number?
var list = document.getElementById('list');
list.style.listStyleType = 'upper-roman';
<ol class="list" id="list">
<li class="list__item">apple</li>
<li class="list__item">banana</li>
<li class="list__item" id="target">orange</li>
<li class="list__item">pear</li>
</ol>
That will produce a list of items like this.
I. apple
II. banana
III. orange
IV. pear
Is there a way to get the III string of text of the #target list item?
EDIT:
Roman characters here are just an example. I'd like the ability to access to the content provided by any of the list-style-type options.
The only way I can think of doing this is the following:
1) Get the index of the item (e.g. 3)
2) Have a function like this: https://stackoverflow.com/a/9083076/1324321
3) Run the index through the function
I created a jsfiddle here which can display the chosen selection to the user. Although javascript is not holding this as a string, I first find the index of the selected list item, then I recreate a list of that one item with the "start" attribute being set to that index.
Here is the HTML:
<ol>
<li>first</li>
<li>second</li>
<li id="active">third</li>
<li>Fourth</li>
</ol>
<br/>
<br/>
<div id='selected'>
</div>
And the JQuery:
$(document).ready(function() {
var intt = $('li').index($('#active')) + 1;
$('#selected').html('<ol start="' + intt + '"><li></li></ol>');
});
JSFIDDLE
You could use the start attribute and iterate all list elements.
var list = document.getElementById('list'),
start = list.start || 0;
list.style.listStyleType = 'upper-roman';
Array.prototype.forEach.call(list.getElementsByTagName('li'), function (a, i) {
if (a.id === 'target') {
console.log(i + start);
console.log(a.innerHTML);
}
});
<ol class="list" id="list" start="5">
<li class="list__item">apple</li>
<li class="list__item" >banana</li>
<li class="list__item" id="target">orange</li>
<li class="list__item">pear</li>
</ol>
Is it possible to get the .nextUntil() to work on split lists, or get the same functionality?
So I am trying to implement the ever so popular shift select for my items, and since they are ordered in a list in my application I want to be able to select across <ul> borders.
I have the following set of DOM elements:
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
And using something like this:
$('li.clicked').nextUntil('li.selected');
I'd like a list containing the following elements
[ <li class="item">third</li>,
<li class="item">fourth</li>,
<li class="item">fifth</li> ]
However all I get is the elements leading up to the split </ul>. Is there any way of doing this? I have also tried to first selecting all items with $('.item')and then using .nextUntil() on them without any luck.
Is this what you are looking for?
$('li').slice($('li').index($('.clicked'))+1,$('li').index($('.selected')));
For reference
Jquery.Index
Jquery.Slice
Edit
So if you do
$('li')
you will get an array of all elements 'li' getting:
[<li class="item">first</li>,
<li class="item clicked">second</li>,
<li class="item">third</li>,
<li class="item">fourth</li>,
<li class="item">fifth</li>,
<li class="item selected">sixth</li>,
<li class="item">seventh</li>]
Since it is an array you can slice him to get an sub array you just need two positions, where to start and here to finish.
//start
$('li').index($('.clicked'))+1 // +1 because you dont want to select him self
//end
$('li').index($('.selected'))
For better preformance you should before create an array with all li so it will not search all dom 3 times for the array of 'li'
var array = $('li');
var subarray = array.slice(array.index($('.clicked'))+1,array.index($('.selected')));
Assuming these lists cannot be merged into one, it is impossible using the nextUntil method. This is because of how jQuery performs traversing. According to the documentation,
Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed.
fifth is not a sibling of the clicked element, but rather it is a child of the sibling of the element's parents.
I came up with two possible solutions.
Solution 1: Combine NEXT and PREV traversals
Assuming that .clicked is always in the first list and .selected is always in the second list, combining prevAll() with nextAll() should do the trick. This assumes that the order is the same.
var siblings = $("li.clicked").nextAll()
Get all siblings of the current element AFTER the element itself.
var distantSiblings = $("li.selected").prevAll();
Get all distant siblings after the first element, but before the second one.
siblings.push(distantSiblings);
Combine them into two and then iterate over each element.
var siblings = $("li.clicked").nextAll()
var distantSiblings = $("li.selected").prevAll();
siblings.push(distantSiblings);
siblings.each(function() {
$(this).addClass("blue");
});
.blue { color: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
http://jsfiddle.net/r15z10o4/
Note:
You will notice that the above code works, however it might not be the optimal solution. This is only confirmed to work for your example above. There may also be a less verbose solution.
Solution 2 (Find index of all list items)
Another idea is to find the index of all items, and collect the elements that are sandwiched between those two indices. You will then want to use the 'slice' selector to get the range in between.
var items = $(".item");
var clicked = $(".clicked");
var selected = $(".selected");
var clickIndex = items.index(clicked);
var selectIndex = items.index(selected);
$("li").slice(clickIndex + 1, selectIndex).addClass("blue");
var clicked = $(".clicked");
var selected = $(".selected");
var clickIndex = $("li").index(clicked);
var selectIndex = $("li").index(selected);
$("li").slice(clickIndex+1, selectIndex).addClass("blue");
.blue { color: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
You can do it manually by selecting all these items at once, and using loops.
Consider the parent element, let's say "container":
<div id="container">
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
</div>
Now, you can select all these items:
var $items = $("#container > ul > li.item"); // or simply $("#container .item");
And iterate through them:
var $items = $(".item"), $result = $(), found = false;
for (var i = 0; i < $items.length; i++)
{
$currentItem = $items.eq(i);
if ($currentItem.is('.clicked')) {
found = true;
continue;
}
if ($currentItem.is('.selected'))
break;
if (found)
$result = $result.add($currentItem);
}
console.log($result);
Here is the working JSFiddle demo.
In any case it feels like you will need to define groups of li.
I think the easiest is to create a function getting a list of lis that you can request any way you want then to filter the el you are interested in.
function elRange(elList, start, end){
// we do not use indexOf directly as elList is likely to be a node list
// and not an array.
var startI = [].indexOf.call(elList, start);
var endI = [].indexOf.call(elList, end);
return [].slice.call(elList, startI, endI + 1);
}
// request the group of *ordered* elements that can be selected
var liList = document.querySelectorAll('ul.current > li, ul.later > li');
var selectionEnd = document.querySelector('.selected');
[].forEach.call(liList, function(li){
li.addEventListener('click', function(){
var selected = elRange(liList, li, selectionEnd);
selected.forEach(function(el){
el.classList.add('red');
})
});
});
.selected {
color: blue;
}
.red {
color: red;
}
<ul class="current">
<li class="item">first</li>
<li class="item">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>