I'm wanting to sort this UL numerically and alphabetically, using each items data-char value.
Note: I'm only wanting to sort the parent UL, not the child UL elements.
<ul>
<li data-char="w">
<span>W</span>
<ul>
<li>WWWWWWWWWWWWWW</li>
</ul>
</li>
<li data-char="5">
<span>5</span>
<ul>
<li>55555555555555</li>
</ul>
</li>
<li data-char="a">
<span>A</span>
<ul>
<li>AAAAAAAAAAAAAA</li>
</ul>
</li>
<li data-char="1">
<span>1</span>
<ul>
<li>11111111111</li>
</ul>
</li>
</ul>
I'm able to acplish this with jQuery by doing:
function sortCharLi(a, b) {
var va = a.dataset.char.toString().charCodeAt(0),
vb = b.dataset.char.toString().charCodeAt(0);
// add weight if it's a number
if (va < 'a'.charCodeAt(0)) va += 100;
if (vb < 'a'.charCodeAt(0)) vb += 100;
return vb < va ? 1 : -1;
}
$('ul > li').sort(sortCharLi).appendTo('ul');
But I'm needing to remove the jQuery dependency so that's not an option any more.
Any ideas how I may do this without jQuery?
JSBIN
I'm wanting to sort this UL numerically and alphabetically, using each items data-char value.
Note: I'm only wanting to sort the parent UL, not the child UL elements.
<ul>
<li data-char="w">
<span>W</span>
<ul>
<li>WWWWWWWWWWWWWW</li>
</ul>
</li>
<li data-char="5">
<span>5</span>
<ul>
<li>55555555555555</li>
</ul>
</li>
<li data-char="a">
<span>A</span>
<ul>
<li>AAAAAAAAAAAAAA</li>
</ul>
</li>
<li data-char="1">
<span>1</span>
<ul>
<li>11111111111</li>
</ul>
</li>
</ul>
I'm able to acplish this with jQuery by doing:
function sortCharLi(a, b) {
var va = a.dataset.char.toString().charCodeAt(0),
vb = b.dataset.char.toString().charCodeAt(0);
// add weight if it's a number
if (va < 'a'.charCodeAt(0)) va += 100;
if (vb < 'a'.charCodeAt(0)) vb += 100;
return vb < va ? 1 : -1;
}
$('ul > li').sort(sortCharLi).appendTo('ul');
But I'm needing to remove the jQuery dependency so that's not an option any more.
Any ideas how I may do this without jQuery?
JSBIN
Share edited May 30, 2016 at 9:19 Pranav C Balan 115k25 gold badges171 silver badges195 bronze badges asked May 24, 2016 at 20:43 dittoditto 6,32710 gold badges58 silver badges91 bronze badges 04 Answers
Reset to default 6 +100You can get the ul
using getElemetsByTagName
and children li
can be get from the element object using children
property.
function order(ul) {
// get html children elements of li
// in case of ul children will be li
// ` Array.from` will hell helps to convert them into array
var elements = Array.from(ul.children);
// sort them with the same code
elements.sort(function(a, b) {
var va = a.getAttribute('data-char').charCodeAt(0),
vb = b.getAttribute('data-char').charCodeAt(0),
charCA = 'a'.charCodeAt(0);
// add weight if it's a number
if (va < charCA) va += 100;
if (vb < charCA) vb += 100;
// just get the difference and return to sort them
return va - vb;
});
// append back to update the order
// forEach can be used to update since it's in array format
elements.forEach(function(ele) {
ul.appendChild(ele)
});
}
// get ul tag from dom and pass as parameter
// although you can use id selector or querySelector, etc
// it depends up on your need, here you just need to pass the dom reference of `ul` to be sorted
order(document.getElementsByTagName('ul')[0]);
<ul>
<li data-char="w">
<span>W</span>
<ul>
<li>WWWWWWWWWWWWWW</li>
</ul>
</li>
<li data-char="5">
<span>5</span>
<ul>
<li>55555555555555</li>
</ul>
</li>
<li data-char="a">
<span>A</span>
<ul>
<li>AAAAAAAAAAAAAA</li>
</ul>
</li>
<li data-char="1">
<span>1</span>
<ul>
<li>11111111111</li>
</ul>
</li>
</ul>
UPDATE : If you want to use them in chain as in jQuery, then extend the prototype
NodeList.prototype.sortElements = function(custom) {
// if custom sort function passed then sort based on that
if (typeof custom === 'function')
return [].slice.call(this).sort(custom);
// otherwise apply sort method directly
return [].slice.call(this).sort();
// you can also use Array.from(this), which only works in latest browsers
}
Array.prototype.updateOrder = function() {
// iterate over array element
this.forEach(function(ele) {
// append to the parent element
ele.parentNode.appendChild(ele);
})
}
// sort them with the same code
function sortFn(a, b) {
var va = a.getAttribute('data-char').charCodeAt(0),
vb = b.getAttribute('data-char').charCodeAt(0),
charCA = 'a'.charCodeAt(0);
// add weight if it's a number
if (va < charCA) va += 100;
if (vb < charCA) vb += 100;
// just get the difference and return to sort them
return va - vb;
}
// get li elements which have `data-char` attribute
document.querySelectorAll('ul li[data-char]')
.sortElements(sortFn) // call sortElements methods we defined with custom sort function
.updateOrder(); // call updateOrder to update the order of element
<ul>
<li data-char="w">
<span>W</span>
<ul>
<li>WWWWWWWWWWWWWW</li>
</ul>
</li>
<li data-char="5">
<span>5</span>
<ul>
<li>55555555555555</li>
</ul>
</li>
<li data-char="a">
<span>A</span>
<ul>
<li>AAAAAAAAAAAAAA</li>
</ul>
</li>
<li data-char="1">
<span>1</span>
<ul>
<li>11111111111</li>
</ul>
</li>
</ul>
This function preserves your original sorter method. The function expects the ul
element to be passed:
function sortThem(ul) {
var nodes = Array.prototype.slice.call(ul.childNodes).filter(function(el) {
// Could use QSA with scope depending on browser support here
return el.tagName === 'LI';
});
nodes.sort(function(a, b) {
var va = a.getAttribute('data-char').charCodeAt(0),
vb = b.getAttribute('data-char').charCodeAt(0);
// add weight if it's a number
if (va < 'a'.charCodeAt(0)) va += 100;
if (vb < 'a'.charCodeAt(0)) vb += 100;
return vb < va ? 1 : -1;
});
nodes.forEach(function(node) {
ul.appendChild(node);
});
}
You may use querySelectorAll for $('ul > li')
. Pay attention, the right selector should be $('ul > li[data-char]')
because you are inrested only in li tags having the data-char as attribute.
To convert the NodeList returned from querySelectorAll to array you can use Array.from.
To substitute the appendTo you may use forEach.
So the code could be:
function sortCharLi(a, b) {
var va = a.getAttribute('data-char').charCodeAt(0),
vb = b.getAttribute('data-char').charCodeAt(0);
// add weight if it's a number
if (va < 'a'.charCodeAt(0)) va += 100;
if (vb < 'a'.charCodeAt(0)) vb += 100;
return vb < va ? 1 : -1;
}
window.onload = function() {
// $('ul > li').sort(sortCharLi).appendTo('ul');
Array.from(document.querySelectorAll('ul > li[data-char]')).sort(sortCharLi).forEach(function(element, index) {
element.parentNode.appendChild(element);
});
}
<ul>
<li data-char="w">
<span>W</span>
<ul>
<li>WWWWWWWWWWWWWW</li>
</ul>
</li>
<li data-char="5">
<span>5</span>
<ul>
<li>55555555555555</li>
</ul>
</li>
<li data-char="a">
<span>A</span>
<ul>
<li>AAAAAAAAAAAAAA</li>
</ul>
</li>
<li data-char="1">
<span>1</span>
<ul>
<li>11111111111</li>
</ul>
</li>
</ul>
UPDATE A shorter way to convert:
$('ul > li[data-char]').sort(sortCharLi).appendTo('ul');
to pure javaScript can be:
document.querySelectorAll('ul > li[data-char]').sort(sortCharLi).replaceWith();
In order to achieve this result the following methods have to be added:
NodeList.prototype.sort = function(callBack) {
if (typeof callBack === 'function') {
return [].slice.call(this).sort(callBack);
} else {
return [].slice.call(this).sort();
}
}
Array.prototype.replaceWith = function() {
this.forEach(function(element, index) {
element.parentNode.appendChild(element);
});
return this;
}
In this way there exist the possibility to chain methods like in jQuery:
function sortCharLi(a, b) {
var va = a.getAttribute('data-char').charCodeAt(0),
vb = b.getAttribute('data-char').charCodeAt(0);
// add weight if it's a number
if (va < 'a'.charCodeAt(0)) va += 100;
if (vb < 'a'.charCodeAt(0)) vb += 100;
return vb < va ? 1 : -1;
}
NodeList.prototype.sort = function(callBack) {
if (typeof callBack === 'function') {
return [].slice.call(this).sort(callBack);
} else {
return [].slice.call(this).sort();
}
}
Array.prototype.replaceWith = function() {
this.forEach(function(element, index) {
element.parentNode.appendChild(element);
});
return this;
}
window.onload = function () {
document.querySelectorAll('ul > li[data-char]').sort(sortCharLi).replaceWith();
}
<ul>
<li data-char="w">
<span>W</span>
<ul>
<li>WWWWWWWWWWWWWW</li>
</ul>
</li>
<li data-char="5">
<span>5</span>
<ul>
<li>55555555555555</li>
</ul>
</li>
<li data-char="a">
<span>A</span>
<ul>
<li>AAAAAAAAAAAAAA</li>
</ul>
</li>
<li data-char="1">
<span>1</span>
<ul>
<li>11111111111</li>
</ul>
</li>
</ul>
This is the best answer I can snap off from work, but it might need to be tailored to your html structure.
FUNCTION TO SORT UL WITHOUT JQUERY:
function sortUnorderedList(ul, sortDescending) {
if(typeof ul == "string")
ul = document.getElementById(ul);
// Idiot-proof, remove if you want
if(!ul) {
alert("The UL object is null!");
return;
}
// Get the list items and setup an array for sorting
var lis = ul.getElementsByTagName("LI");
var vals = [];
// Populate the array
for(var i = 0, l = lis.length; i < l; i++)
vals.push(lis[i].innerHTML);
// Sort it
vals.sort();
// Sometimes you gotta DESC
if(sortDescending)
vals.reverse();
// Change the list on the page
for(var i = 0, l = lis.length; i < l; i++)
lis[i].innerHTML = vals[i];
}
USAGE:
sortUnorderedList("ID_OF_LIST");