Sorting a list by data-attribute - javascript

I have a list of people with job titles sorted by the persons’ first names, like this:
<ul>
<li data-azsort="smithjohn">
<a href="#">
<span class="list-name">John Smith</span>
</a>
<span class="list-desc">Professor</span>
</li>
..
<li data-azsort="barnestom">
<a href="#">
<span class="list-name">Tom Barnes</span>
</a>
<span class="list-desc">Lecturer</span>
</li>
</ul>
I’ve added the data-azsort attribute to the <li> element, and I’d like to pop these list elements into an array, and sort based on that data-* attribute (using plain JavaScript).
What would be the best way to sort the list by data-azsort (A-Z), returning the same code? JavaScript only, no jQuery, etc.

This works for any number of lists: it basically gathers all lis in uls that have your attribute, sorts them according to their data-* attribute value and re-appends them to their parent.
Array.from(document.querySelectorAll("ul > li[data-azsort]"))
.sort(({dataset: {azsort: a}}, {dataset: {azsort: b}}) => a.localeCompare(b)) // To reverse it, use `b.localeCompare(a)`.
.forEach((item) => item.parentNode.appendChild(item));
<ul>
<li data-azsort="skeetjon">
<span class="list-name">Jon Skeet</span>
<span class="list-desc">Stack Overflow user</span>
</li>
<li data-azsort="smithjohn">
<span class="list-name">John Smith</span>
<span class="list-desc">Professor</span>
</li>
<li data-azsort="barnestom">
<span class="list-name">Tom Barnes</span>
<span class="list-desc">Lecturer</span>
</li>
</ul>
<ul>
<li data-azsort="smithjohn">
<span class="list-name">John Smith</span>
<span class="list-desc">Professor</span>
</li>
<li data-azsort="barnestom">
<span class="list-name">Tom Barnes</span>
<span class="list-desc">Lecturer</span>
</li>
<li data-azsort="skeetjon">
<span class="list-name">Jon Skeet</span>
<span class="list-desc">Stack Overflow user</span>
</li>
</ul>
The funny thing is, it gets all lis in the same array, sorts them all, but in the end figures out which list the li originally belonged to. It’s a pretty simple and straight-forward solution.
If you want to sort elements by a numeric data attribute, then use this sort function instead:
// Presumably, the data-* attribute won’t be called `azsort`. Let’s call it `numsort`.
({dataset: {numsort: a}}, {dataset: {numsort: b}}) => Number(a) - Number(b) // `Number(b) - Number(a)` to reverse the sort.
A slightly longer ECMAScript 5.1 alternative would be:
Array.prototype.slice.call(document.querySelectorAll("ul > li[data-azsort]")).sort(function(a, b) {
a = a.getAttribute("data-azsort");
b = b.getAttribute("data-azsort");
return a.localeCompare(b);
}).forEach(function(node) {
node.parentNode.appendChild(node);
});

What about getting all of the list items, push them into array which later will be sorted?
var allListElements = document.getElementById("staff").getElementsByTagName("li");
var staff = new Array();
for (i = 0; i < allListElements.length; i++) {
staff.push(allListElements[i].getAttribute('data-azsort'));
}
staff.sort(function(a, b) {
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
//Print
document.write('<h4>Sorted</h4>');
for (i = 0; i < staff.length; i++) {
document.write(staff[i] + "<br />");
}
<h4>Input</h4>
<ul id="staff">
<li data-azsort="smithjohn">
<a href="#">
<span class="list-name">John Smith</span>
</a>
<span class="list-desc">Professor</span>
</li>
<li data-azsort="barnestom">
<a href="#">
<span class="list-name">Tom Barnes</span>
</a>
<span class="list-desc">Lecturer</span>
</li>
</ul>
Additionally you can save the index of <li> and reorder the <ul>.

You can pass a comparison function to Array.prototype.sort
you should be able to do something like
$items = $('li[data-azsort]');
var compareElem = function (a, b) {
if (a.attr('data-azsort') > b.attr('data-azsort') {
return 1;
} else {
return -1
}
};
Array.prototype.sort.apply($items, compareElem);

Related

Swap "ul li" dynamically

I have my ul : li list with a span named as badge which contains the total number of unread messages. So i want to shuffle all the li items with the highest number on top of the list and lowest or none to the last. I tried many solutions but still can`t get it. Another point is the count gets update live so the list should also shuffle live. Here is the code that i tried till now.
My HTML Code
<li>
<span class="badge" style="display:none" id="61_T">0</span>
</li>
<li>
<span class="badge" style="display:none" id="62_T">5</span>
</li>
<li>
<span class="badge" style="display:none" id="63_T">10</span>
</li>
<li>
<span class="badge" style="display:none" id="64_T">0</span>
</li>
Here is my JS Code
var prev_index = 0;
var curr_index = 0;
var curr_val = 0;
var prev_val = 0;
var lists = $('#items li');
var msg_count = [];
$('#items li').each(function(){
var current_index = $(this).index();
var count = $(this).find('.badge').text();
msg_count.push([current_index,count]);
});
updateli();
function updateli(){
$.each(msg_count,function(key,value){
var str = value.join('-');
var sep = str.split('-');
curr_index = sep[0];
curr_val = parseInt(sep[1]);
if(curr_val > prev_val){
$("#items li:eq("+curr_index+")").siblings().eq(curr_index).after(lists.siblings(':eq('+prev_index+')'));
}
prev_index = curr_index;
prev_val = curr_val;
});
}
What i did here is created an array with li index and unread count number. After than looped the array and used jQuery function to swap the elements but nothing seems to work. Any help will really appreciated. Thanks in advance.
This sorts the list without using JQuery
function sortItems(containerSelector, itemSelector, countSelector, asc) {
let container = document.querySelector(containerSelector);
let items = [].slice.call(container.querySelectorAll(itemSelector));
items.sort(function(currItem, nextItem) {
var currCountElement = currItem.querySelector(countSelector);
var nextCountElement = nextItem.querySelector(countSelector);
if(!currCountElement) return 1;
if(!nextCountElement) return -1;
var currCount = parseInt(currCountElement.textContent || -1);
var nextCount = parseInt(nextCountElement.textContent || -1);
var order = currCount - nextCount;
return asc?-order:order;
});
items.forEach(function(item) { container.appendChild(item)});
}
// demo code
[].slice.call(document.querySelectorAll('.sortButton')).forEach(function(button) {
button.addEventListener('click', function(e) { sortItems('.items', 'li', '.badge', this.classList.contains('-desc')) });
});
<ul class="items">
<li>
2
<span class="badge" style="display:none" id="61_T">2</span>
</li>
<li>
5
<span class="badge" style="display:none" id="62_T">5</span>
</li>
<li>
10
<span class="badge" style="display:none" id="63_T">10</span>
</li>
<li>
1
<span class="badge" style="display:none" id="63_T">1</span>
</li>
<li>
0
<span class="badge" style="display:none" id="64_T">0</span>
</li>
<li>
none
<span class="badge" style="display:none" id="64_T"></span>
</li>
<li>
no badge
</li>
</ul>
<button class="sortButton">asc</button>
<button class="sortButton -desc">desc</button>
Edit: made it a method
try the code below please.jquery return a array-like object so you can sort elements by sort(..) method,and then replace all the li.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<span class="badge" id="61_T">0</span>
</li>
<li>
<span class="badge" id="62_T">5</span>
</li>
<li>
<span class="badge" id="63_T">10</span>
</li>
<li>
<span class="badge" id="64_T">0</span>
</li>
</ul>
<script>
var badges = $('.badge').parent().sort(function (a, b) {
return parseInt($(b).find('span').text()) - parseInt($(a).find('span').text());
});
badges.closest('ul').empty().append(badges);
</script>

changing span text in li using angularjs

I have html something like...
<ul id="sidemenu" class="wraplist wrapper-menu">
<li class="auto" ng-class='{"active": active == 1 }' ng-click="makeActive(1)">
<span class="arrow material-icons">arrow_down</span>
<li>
<li class="auto" ng-class='{"active": active == 2 }' ng-click="makeActive(2)">
<span class="arrow material-icons">arrow_down</span>
<li>
<ul>
on ng-click=makeActive(), I want to change the value 'arrow_down' to 'arrow_right' of that particular < li> element only. and if again the same is clicked I want to change it to 'arrow_down'. Rest all < li> will have span text unchanged. How can I do this using angularjs? i.e. by using angular.element? OR is there any other way?
keyboardArrow refers to only one variable. So you have to create two scope variables: keyboardArrow1 and keyboardArrow2
<ul id="sidemenu" class="wraplist wrapper-menu">
<li class="auto" ng-class='{"active": active == 1 }' ng-click="makeActive(1)">
<span class="arrow material-icons">{{ keyboardArrow1 }}</span>
<li>
<li class="auto" ng-class='{"active": active == 2 }' ng-click="makeActive(2)">
<span class="arrow material-icons">{{ keyboardArrow2 }}</span>
<li>
<ul>
Update
According to your needs, here is a plunker.
Updated my answer from our discussion in comments.
1) In your controller, define keyboardArrow1 and keyboardArrow2:
$scope.keyboardArrow1 = 'A';
$scope.keyboardArrow1 = 'B';
2) You can now display theses values like this:
<ul>
<li ng-class="{'active': active == keyboardArrow1}" ng-click="makeActive(keyboardArrow1)">
{{keyboardArrow1}}
</li>
<li ng-class="{'active': active == keyboardArrow2}" ng-click="makeActive(keyboardArrow2)">
{{keyboardArrow2}}
</li>
</ul>
Note that I also changed the ng-class condition.
3) When clicking a <li>, call makeActive():
$scope.makeActive = function(arrow) {
$scope.active = arrow;
}

Color Scale over List

I have an unordered list that may contain up to 10 items. Within each li is a numeric value. I want the first number to be green, the last number to be yellow, and each number between to be scaled between the two accordingly.
I'm curious if this can be done with css3 alone, or if I'd need JavaScript to calculate the interim and apply it (I could probably work out a JS solution on my own, but I don't know it's worth it at the moment)
Essentially I'd replace the number-good classes (e.g., green) with the scaled value instead.
<ul id="dash-top-customer-list" class="tb-list">
<li class="tb-list-item" data-dym-id="8">
<span class="tb-list-name">OEM2</span><span class="tb-list-value number-good">$208,057</span>
</li>
<li class="tb-list-item" data-dym-id="13">
<span class="tb-list-name">Potential3</span><span class="tb-list-value number-good">$206,988</span>
</li>
<li class="tb-list-item" data-dym-id="9">
<span class="tb-list-name">REP1</span><span class="tb-list-value number-good">$191,029</span>
</li>
<li class="tb-list-item" data-dym-id="14">
<span class="tb-list-name">Potential4</span><span class="tb-list-value number-good">$187,609</span>
</li>
<li class="tb-list-item" data-dym-id="15">
<span class="tb-list-name">Potential5</span><span class="tb-list-value number-good">$183,372</span>
</li>
</ul>
(this is hardly a must-have, and the question is more a curiosity than anything - I was surprised I couldn't find a pertinent question already asked (or my google-fu is really in the can today))
If your list is changing, and you want to recalculate the values, you might want to consider JavaScript. Especially if you want to order the list. This sounds a lot like what d3.js helps with, so it might be worthwhile to have a look into that as it allows you to create color scales.
Another option in JS would be to set up an array of colors and then have a switch statement that assigns colors to the DOM elements.
If it isn't dynamic, then you can simply set up 10 color / classes in your stylesheet and give the DOM elements the class that you want each element to have.
Something like this perhaps?
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
colorizeListItems( document.getElementById('dash-top-customer-list'), -60, 120 );
}
function colorizeListItems(targetUl, hueRange, hueMin)
{
var liNodeList = targetUl.getElementsByTagName('li');
var liArray = [].slice.call(liNodeList);
var valueArray = liArray.map( function(liElem) { return liElem.querySelectorAll('span')[1].textContent.replace(/,*\$*/g, ''); } );
var largest,smallest,difference;
largest = smallest = valueArray[0];
valueArray.forEach( function(curVal) {if (curVal < smallest) smallest = curVal; if (curVal > largest) largest = curVal;} );
difference = largest - smallest;
liArray.forEach( function(elem, index)
{
var curScaledVal = (valueArray[index] - smallest) / difference;
var curHue = (curScaledVal * hueRange) + hueMin;
var curStr = "hsl(" + curHue.toFixed(0) + ", 100%, 50%);";
elem.setAttribute('style', 'background: ' + curStr);
}
);
}
<ul id="dash-top-customer-list" class="tb-list">
<li class="tb-list-item" data-dym-id="8">
<span class="tb-list-name">OEM2</span><span class="tb-list-value number-good">$8,000,000</span>
</li>
<li class="tb-list-item" data-dym-id="8">
<span class="tb-list-name">OEM2</span><span class="tb-list-value number-good">$5,000,000</span>
</li>
<li class="tb-list-item" data-dym-id="8">
<span class="tb-list-name">OEM2</span><span class="tb-list-value number-good">$6,000,000</span>
</li>
<li class="tb-list-item" data-dym-id="8">
<span class="tb-list-name">OEM2</span><span class="tb-list-value number-good">$7,000,000</span>
</li>
</ul>

Trying to find the nearest date today in a <ul>

I am trying to find the next/nearest date to today from a list of dates and then add a class to it and display that item in another place.
<ul class="locations">
<li>
<span class="date">01-Jun-2015</span>
<span class="location">London</span>
</li>
<li>
<span class="date">15-Jun-2015</span>
<span class="location">Paris</span>
</li>
<li>
<span class="date">03-Jul-2015</span>
<span class="location">Berlin</span>
</li>
<li>
<span class="date">16-Jun-2015</span>
<span class="location">Milan</span>
</li>
<li>
<span class="date">20-Jul-2015</span>
<span class="location">Madrid</span>
</li>
<li>
<span class="date">07-Aug-2015</span>
<span class="location">Lisbon</span>
</li>
</ul>
<p class="next-date">
Next date and location:
</p>
Please can someone help point me in the right direction?
I assume you'd be okay with jQuery as your question is tagged so. You could find the nearest future date to today using a couple of in-built functions, such as Date(), Array.sort() and $.map() as below. Pls look at the comments in the script for more info about the code.
var $locations = $(".locations");
//Today's date in milliseconds
var tsToday = new Date().getTime();
//Create an array of timestamps using .map
var allDatesTimestamp = $locations.find(".date").map(function() {
//Convert text to date in milliseconds
var ts = new Date($(this).text()).getTime();
//Return only those timestamps that are greater than today
//And sort them to get the smallest/nearest timestamp as the first array item
if (ts > tsToday) {
return ts;
}
}).get().sort();
//Find all .date elements and filter out
var $elem = $locations.find(".date").filter(function() {
//Filter out the one where date equals to first item in the array as that's the nearest
return new Date($(this).text()).getTime() === allDatesTimestamp[0]
//Return the sarrounding element
//Add a class if need be, to highlight it
}).closest("li").addClass("nearest");
//Rest is simple; find and display.
$(".next-date")
.append("Date: " + $elem.find(".date").text())
.append("<br/>Location: " + $elem.find(".location").text());
.locations .nearest {
color: green;
font-size: 1.5em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="locations">
<li>
<span class="date">01-Jun-2015</span>
<span class="location">London</span>
</li>
<li>
<span class="date">15-Jun-2015</span>
<span class="location">Paris</span>
</li>
<li>
<span class="date">03-Jul-2015</span>
<span class="location">Berlin</span>
</li>
<li>
<span class="date">16-Jun-2015</span>
<span class="location">Milan</span>
</li>
<li>
<span class="date">20-Jul-2015</span>
<span class="location">Madrid</span>
</li>
<li>
<span class="date">07-Aug-2015</span>
<span class="location">Lisbon</span>
</li>
</ul>
<p class="next-date">
<strong>Next date and location: </strong><br/>
</p>
Hope that helps.
I wrote a custom sort function to sort the closet next date
dates.sort(function(x, y) {
if (x[0]-Date.now() < 0) {
return 1;
}
if (x[0]-Date.now() < y[0]-Date.now()) {
return -1;
}
if (x[0]-Date.now() > y[0]-Date.now()) {
return 1;
}
return 0;
});
http://codepen.io/anon/pen/Kpqvxv
Try using Date() for comparison:
$(function() {
var now = new Date().getTime(); //get current time
var li, diff, sp, tmp;
$('span.date').each(function() {//loop in all dates
sp = $(this);
if (!li) {// first time
li = sp.closest('li'); // get parent of date
diff = Math.abs(now - new Date(sp.text()).getTime()); //get differnce
return;
}
tmp = Math.abs(now - new Date(sp.text()).getTime());
if (tmp < diff) { // this is close to current date
diff = tmp;
li = sp.closest('li');
}
});
li.addClass('now'); // highlight
$('p.next-date').text(function() {
return this.innerHTML + li.text(); // update date and place
});
});
.now {
background-color: orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul class="locations">
<li>
<span class="date">01-Jun-2015</span>
<span class="location">London</span>
</li>
<li>
<span class="date">15-Jun-2015</span>
<span class="location">Paris</span>
</li>
<li>
<span class="date">03-Jul-2015</span>
<span class="location">Berlin</span>
</li>
<li>
<span class="date">16-Jun-2015</span>
<span class="location">Milan</span>
</li>
<li>
<span class="date">20-Jul-2015</span>
<span class="location">Madrid</span>
</li>
<li>
<span class="date">07-Aug-2015</span>
<span class="location">Lisbon</span>
</li>
</ul>
<p class="next-date">
Next date and location:
</p>

how to get li first href in a UL using javascript

everyone i want to get all first href elements in li and i want it in javascript only
<div id="cataloge">
<ul id="catalog-items" class="product-items">
<li id="OS389HL91QJCLMX" class="product-item >
<a id="1:OS389HL91QJCLMX" href="/piezas-y-Sala-4-piezas%29.-116508.html">link1
<ul><li>some data</li><li>some data</li><li>some data</li></ul>
</a>
</li>
<li id="OS389HL91QJCLMX" class="product-item >
<a id="1:OS389HL91QJCLMX" href="/piezas-y-Sala-4-piezas%29.-116508.html">link1
<ul><li>some data</li><li>some data</li><li>some data</li></ul>
</a>
</li>
<li id="OS389HL91QJCLMX" class="product-item >
<a id="1:OS389HL91QJCLMX" href="/piezas-y-Sala-4-piezas%29.-116508.html">link1
<ul><li>some data</li><li>some data</li><li>some data</li></ul>
</a>
</li>
</ul>
<div>
i want get href links in the li classname "product-item" under UL with id catalog- items
i written this below code,but it is giving empty when print the chethan1;
var lists = document.getElementById("catalog-items");
var items = lists.getElementsByTagName("li");
var chethan1="";
for (var i = 0; i < items.length; i++)
{
if(typeof items[i]!="undefined" && items[i]!="" && items[i].className=="product-item")
{
chethan1.push(items[i]);
}
}
console.log(chethan1);
[empty]
plz someone help me to sort out..thanks in advance..
See this: http://jsfiddle.net/M5xYb/
var chethan1= new Array();
Closes string, uses new array from above. Results are not empty.
Among other markup concerns, .push() is to be used on arrays, not strings.
Use var chethan1 = []; instead of var chethan1="";.
Updated Fiddle

Categories