Reputation: 35734
Given the following list
<ul class="listitems">
<li data-position="1">Item 1</li>
<li data-position="2">Item 2</li>
<li data-position="3">Item 3</li>
<li data-position="4">Item 4</li>
</ul>
there is some functionality on the page that will allow the possibility of these items changing position. For example, they may get to the following state (example only, the order could be anything):
<ul class="listitems">
<li data-position="3">Item 3</li>
<li data-position="2">Item 2</li>
<li data-position="1">Item 1</li>
<li data-position="4">Item 4</li>
</ul>
I am looking for a small function to reset the order. So far I have the following:
function setPositions()
{
$( '.listitems li' ).each(function() {
var position = $(this).data('position');
$(this).siblings().eq(position+1).after(this);
});
}
But it isnt working correctly. What am i doing wrong?
An additonal condition is that the order of the list might not have changed, and so the function has to work in that scenario also.
Upvotes: 53
Views: 97402
Reputation: 40639
Try to use sort()
with appendTo()
,
$(".listitems li").sort(sort_li) // sort elements
.appendTo('.listitems'); // append again to the list
// sort function callback
function sort_li(a, b){
return ($(b).data('position')) < ($(a).data('position')) ? 1 : -1;
}
Snippet:
$(function() {
$(".listitems li").sort(sort_li).appendTo('.listitems');
function sort_li(a, b) {
return ($(b).data('position')) < ($(a).data('position')) ? 1 : -1;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul class="listitems">
<li data-position="3">Item 3</li>
<li data-position="2">Item 2</li>
<li data-position="1">Item 1</li>
<li data-position="4">Item 4</li>
</ul>
Upvotes: 132
Reputation: 2216
Try this
function sortList() {
var list, i, switching, b, shouldSwitch;
list = document.getElementById("id01");
switching = true;
/* Make a loop that will continue until
no switching has been done: */
while (switching) {
// start by saying: no switching is done:
switching = false;
b = list.getElementsByTagName("LI");
// Loop through all list-items:
for (i = 0; i < (b.length - 1); i++) {
// start by saying there should be no switching:
shouldSwitch = false;
/* check if the next item should
switch place with the current item: */
//alert(b[i].getAttribute("order"));
if (b[i].getAttribute("order").toLowerCase() > b[i + 1].getAttribute("order").toLowerCase()) {
/* if next item is alphabetically
lower than current item, mark as a switch
and break the loop: */
shouldSwitch = true;
break;
}
}
if (shouldSwitch) {
/* If a switch has been marked, make the switch
and mark the switch as done: */
b[i].parentNode.insertBefore(b[i + 1], b[i]);
switching = true;
}
}
}
<button onclick="sortList()">Sort List</button>
<ul id="id01">
<li order="A" class="disname disheader">Pending</li>
<li order="AA" class="disname">302-1 Energy Consumption</li>
<li order="AA" class="disname">302-3 Energy Intensity</li>
<li order="DD" class="disname">EN-31 Environmental Expenditure/Investment</li>
<li order="DD" class="disname">103-2 Environmental Grievances</li>
<li order="D" class="disname disheader">Deactive</li>
<li order="BB" class="disname">305-4 Emission Intensity</li>
<li order="BB" class="disname">306-2 Total Waste</li>
<li order="BB" class="disname">307-1 Compliance</li>
<li order="AA" class="disname">302-4 Energy/Electricity Reduction Initiative</li>
<li order="AA" class="disname">303-1 Water Withdrawal by Source</li>
<li order="AA" class="disname">303-3 Recycled Water</li>
<li order="C" class="disname disheader">Auto Generated</li>
<li order="CC" class="disname">305-1 GHG Emission</li>
<li order="CC" class="disname">305-2 GHG Emission</li>
<li order="CC" class="disname">305-3 GHG Emission</li>
<li order="BB" class="disname">05-5 Reduction of GHG Emissions</li>
<li order="BB" class="disname">306-1 Water Discharge</li>
<li order="B" class="disname disheader">Overdue</li>
</ul>
Upvotes: 1
Reputation: 25
What about using sort
and replace inside DOM
function sortLiElements(a,b) {
return parseInt($(a).data('position')) - parseInt($(b).data('position'));
}
$('.listitems').html($('.listitems li').sort(sortLiElements));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<ul class="listitems">
<li data-position="3">Item 3</li>
<li data-position="2">Item 2</li>
<li data-position="1">Item 1</li>
<li data-position="4">Item 4</li>
</ul>
Upvotes: 0
Reputation: 184
Expanding on Rohan's answer, if you want this to work for multiple lists rather than just one, you can use the following:
HTML:
<ul class="listitems autosort">
<li data-position="3">Item 3</li>
<li data-position="2">Item 2</li>
<li data-position="1">Item 1</li>
<li data-position="4">Item 4</li>
</ul>
<ul class="listitems autosort">
<li data-position="5">Item 5</li>
<li data-position="6">Item 6</li>
<li data-position="3">Item 3</li>
<li data-position="4">Item 4</li>
</ul>
Javascript:
$(".listitems.autosort").each(function(){
$(this).html($(this).children('li').sort(function(a, b){
return ($(b).data('position')) < ($(a).data('position')) ? 1 : -1;
}));
});
That will allow you to add as many lists as you like and sort them all by just setting the class autosort.
Upvotes: 13
Reputation: 42054
My proposal, in full javascript, is:
document.addEventListener("DOMContentLoaded", function(e) {
Array.prototype.slice.call(document.querySelectorAll('.listitems li')).sort(function(a, b) {
return a.getAttribute('data-position').localeCompare(b.getAttribute('data-position'));
}).forEach(function(currValue) {
currValue.parentNode.appendChild(currValue);
});
});
<ul class="listitems">
<li data-position="3">Item 3</li>
<li data-position="2">Item 2</li>
<li data-position="1">Item 1</li>
<li data-position="4">Item 4</li>
</ul>
Upvotes: 6