I spent some time figuring out a way to sort unordened lists, when dynamically (AJAX) adding/removing items. I had some problems with the typical '10 being greater then 1, but lesser then 2', so I took a simple bubblesort algorithm and tweaked it with the string length. It's probably not the fastest or most elegant way of doing it, but it works like a charm. If you have any improvement points, don't be shy and drop a line.
The algorithm:
This is the search algorithm I used, (which I found here) and I simply added the difference in string length to the equation to make sure that '10' is greater then '1', causing human-natural sorting.
// Search algorithm, if lesser then 0 it's smaller then the current item.
// If it's greater then 0, it's "larger" then the current item.
cmpr = (
(newItem > curListItem) - (curListItem > newItem)
+
(newItem.length - curListItem.length)
);
Before: Title 1, Title 10, Title 13, Title 2, Title 3, Title 4
After: Title 1, Title 2, Title 3, Title 4, Title 10, Title 13
Read on to see a implementation example...
This implementation is done in jQuery, but it should be obvious enough to implemented it in native Javascript, or in any other JS framework:
The Javascript:
My.Select = {
init: function () {
$('div#item_select ul.sub-links>li>a').bind('click', My.Select.bind_action);
},
bind_action: function () {
var el = $(this);
// Performing a post request, using the href attribute of the anchor
jQuery.post(
el.attr('href'),
function (data) {
// If we received some data
if (data !== '') {
data = '</p><li>'+ data +'</li><p>';
var nel = $(data);
var url = nel.children('a').attr('href');
if (url !== '') {
// Determine the ID based on the URL.
var id = (/\/del\//.test(url)) ? '#item_select_container' : '#item_select_selected';
var sortel = nel.children('a').html();
var cmpr = 0;
// If we have at least one element, we can figure out where to place it in the list.
// else we just append it to the ul element
if ($('div'+ id +' ul.sub-links li a').length >= 1) {
$('div'+ id +' ul.sub-links li a').each(function() {
// Search algorithm, if lesser then 0 it's insertBefore, if it's higher then 0 it's insertAfter
cmpr = (
(sortel > $(this).html()) - ($(this).html() > sortel)
+
(sortel.length - $(this).html().length)
);
// insertBefore bails the loop
if (cmpr <= -1) {
nel.insertBefore( $(this).parent('li') );
return false;
} else if (cmpr >= 1) {
nel.insertAfter( $(this).parent('li') );
}
});
} else {
nel.appendTo($('div'+ id +' ul.sub-links'));
}
// Re-Binding the action listener to the newly added anchors
nel.children('a').bind('click', My.Select.bind_action);
// Removing the old element (we got a new one from our AJAX call)
el.parent('li').remove();
// If we have no items in the fancy 'selected' div container, we hide it with a fancy effect
// Or show it if we have > 0 items in it.
if ($('div#item_select_selected ul.sub-links li').length === 0) {
$('div#item_select_selected').hide(200);
} else {
$('div#item_select_selected').show(200);
}
}
}
}
);
return false;
}
};
$(document).ready(function () {
My.Select.init();
}
The used HTML:
<div id="item_select">
<div id="item_select_selected">
<ul class="sub-links">
<li><a href="/del/4" mce_href="/del/4">Title 4</a></li>
</ul>
</div>
<div id="item_select_container">
<ul class="sub-links">
<li><a href="http://blog.dynom.nl/add/1">Title 1</a></li>
<li><a href="http://blog.dynom.nl/add/2">Title 2</a></li>
<li><a href="http://blog.dynom.nl/add/3">Title 3</a></li>
<li><a href="http://blog.dynom.nl/add/10">Title 10</a></li>
<li><a href="http://blog.dynom.nl/add/13">Title 13</a></li>
</ul>
</div>
</div>