Get computed number of ordered list item - javascript

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>

Related

Use Javascript to add a character after the text in a list element, but before an indented list and without spaces between the text and the addition

Is there a way to add with regular Javascript to add a character to the text in a list element, but before an indented list occurs AND to have said character to not have an space between it and the original text? The actual text I have to use is not fixed.
Like, if I have this HTML:
<ul>
<li>Uno
<ul>
<li>One</li>
</ul>
</li>
<li>Dos
<ul>
<li>Two</li>
</ul>
</li>
</ul>
Use Javascript only to add a colon immediately after «Uno» and «Dos», like:
Uno:
One
Dos:
Two
But avoiding this result, which has an space in it (and pretty much any solution I can think of has that issue. I wouldn't had much control over the HTML as I want to use it between a regular WordPress page):
Uno :
One
Dos :
Two
//Get all LI which are immediate child of root UL
let immediateLi = document.querySelectorAll('body > ul > li');
//loop through the Li array and edit text node.
for(let li of immediateLi) {
li.childNodes[0].textContent = li.childNodes[0].textContent.trim() + ':'
}
//Important Note:
//I have selected 0th child-node from the array because it is the default text-node containing our text. In case you are not sure if 0th child-node will hold the text content then you may filter the child-node array.
<ul>
<li>Uno
<ul>
<li>One</li>
</ul>
</li>
<li>Dos
<ul>
<li>Two</li>
</ul>
</li>
</ul>
Here you go. I've also accounted for the case where the list item doesn't contain a nested unordered list.
document.querySelectorAll("ul li:not(ul ul li)").forEach(e => {
let html = e.innerHTML;
var index = html.indexOf("<ul>");
if(index == -1)
{
index = html.trim().length;
}
else
{
while(html[--index] == " " && index > 0){}
}
e.innerHTML = html.substr(0, index) + ":" + (
html.length > index ?
html.substr(index) :
""
);
});
<ul>
<li>Uno
<ul>
<li>One</li>
</ul>
</li>
<li>Dos
<ul>
<li>Two</li>
</ul>
</li>
<li>Tres </li>
</ul>

Concatenate a list of numbers into a list with .innerHTML

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}`
}

Jquery - Filtering cascaded list based on class (Seprate list and nested list)

This is my sample HTML trying achieve some filtering logic for business functionality. We have a Complex structure HTML and i just hard coded some sample HTML to make some example out of it
<ol class="list_1">
<li class="page_1">one</li>
<li class="page_1">two</li>
<li><span class="ing_page_1">three
</span><ol class="list_2">
<li class="page_1">sub 1</li>
<li class="page__1">sub 2</li>
<li class="page__2">sub 3</li>
</ol>
</li>
<li class="page_2">four</li>
<li class="page_2">five</li>
</ol>
Am trying to Filter the list to get below results(DOM Elements).
Query to get DOM elements as results so i can add some attribute for each list
List 1 and its pages(List 1 - One, Two , Three , Four , Five)
List 2 and its pages(List 2 - Sub 1,Sub 2 , Sub 3)
Class 'ing_page_1' also belongs to page 1 but when i have nested structure i have some special class created with ing_ to process some other logic and it is a constant
have added my fiddle(https://jsfiddle.net/pjagana/ubk8ngha/6/) with console statements as my filter always result in sub list as well. Any help would be helpful
You must iterate through all immediate child lis and remove non span (or attr*=ing_page) then get the text.
Maybe you can try this:
function getList(parent) {
var temp = parent + " and its pages (" + parent + " - ";
$("." + parent + ' > li').each(function() { // Find all immediate li children
// Create a clone
var $clone = $(this).clone();
// remove non-span elements and remove them so only span elements would be left (you can replace this with ':not([class&=ing_page])' or something similar if needed)
$clone.find(':not(span)').remove();
// clean the text
temp += $.trim($clone.text().replace(/\s{2,}/gim, " "));
if ($(this).is(':not(:last-child)'))
temp += ", ";
});
return temp + ")";
}
console.log(getList('list_1') + "\n" + getList('list_2'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ol class="list_1">
<li class="page_1">one</li>
<li class="page_1">two</li>
<li>
<span class="ing_page_1">three</span>
<ol class="list_2">
<li class="page_1">sub 1</li>
<li class="page__1">sub 2</li>
<li class="page__2">sub 3</li>
</ol>
</li>
<li class="page_2">four</li>
<li class="page_2">five</li>
</ol>

Using jQuery .nextUntil() on split lists

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>

How to order a list in HTML based on the list item id

I'm sure that this is similar to another question that has been asked, but I looked for a solid hour and couldn't find anything quite right.
I have an un-ordered list where I want the list items to have have an index, so I'm just making their ID to be the index I desire. So it's going to look something like:
<ul>
<li id="1"> item1 </li>
<li id="2"> item2 </li>
<li id="3"> item3 </li>
<li id="4"> item4 </li>
</ul>
Now I have a div that works as a drop down box, which changes the theme of the page as you click on each list item. What I want, is something that will be able to change the id of the list item, and than re-order them so that the selected one is now the first item. So lets assume that I clicked on item3, what I'd ideally like to be the result is something that looks like.
<ul>
<li id="1"> item3 </li>
<li id="2"> item1 </li>
<li id="3"> item2 </li>
<li id="4"> item4 </li>
</ul>
Granted, I'm sure there are other ways of doing it, but I'm trying to change the framework of the project as little as possible, so I'm thinking that this is ideally what I want. The content for each item should obviously stay the same, but the id will change based on where in the list it should be. I hope this isn't too obscure, it seems like something that shouldn't be overly hard but I can't figure it out since I'm new to coding JS / html.
Javascript solution:
Demo
var ul = document.getElementById("list");
var list = ul.children;
for(var i=0; i<list.length; i++){
list[i].onclick = function(){
if(i > 0){
ul.removeChild(this);
ul.insertBefore(this, list[0]);
for(var j=0; j<list.length; j++){
list[j].id = j+1;
}
}
};
}
Use flex-box layout and order property. You can ordering your html elements using only CSS.
Here is a simple snippet:
ul.ordered {
display: flex;
flex-direction: column;
}
<h1>Without Flex</h1>
<ul>
<li id="4"> item 4 </li>
<li id="2"> item 2 </li>
<li id="1"> item 1 </li>
<li id="3"> item 3 </li>
</ul>
<h1>With Flex Order</h1>
<ul class="ordered">
<li id="4" style="order: 4"> item 4 </li>
<li id="2" style="order: 2"> item 2 </li>
<li id="1" style="order: 1"> item 1 </li>
<li id="3" style="order: 3"> item 3 </li>
</ul>
Reference:
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Ordering_Flex_Items
Try this logic... It might works... Please check this Fiddle for demo..
$('li').on({
click: function (e) {
var all = $("#rootUl").children("li");
var index = all.index($(e.target));
var current = e.target.innerHTML;
if (index > 0) {
for (var i = index - 1; i >= 0; i--) {
all[i + 1].innerHTML = all[i].innerHTML;
}
}
all[0].innerHTML = current;
}
});

Categories