Sorting Elements with jQuery

Whilst refactoring a jQuery plugin today, I came across a method that placed a list item into an unordered list at a specific point, so that all the items remained in alphabetic order. This long method seemed completely convoluted and slow to me and I decided that there must be an easier way to keep the list in alphabetical order.

There are existing plugins for sorting elements but I never like to just pile on the 3rd party plugins as that means more javascript files to include in a page. Besides, I was sure it should not be difficult.

It soon occured to me that jQuery has the built-in ability to return the elements as an array, using the .get() method and from there, it was not too long before I had my streamlined code to sort the list element alphabetically:

var mylist = $('ul');
var listitems = mylist.children('li').get();
listitems.sort(function(a, b) {
   var compA = $(a).text().toUpperCase();
   var compB = $(b).text().toUpperCase();
   return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
})
$.each(listitems, function(idx, itm) { mylist.append(itm); });

I utilise the javascript array.sort method to sort the elements in the array and then using the jQuery append() method,ย  reorder them in the actual list element.

 

66 thoughts on “Sorting Elements with jQuery

  1. Thanks a lot, solved my problem.
    Based on that I made the function above to sort by attributes:

    function sortElementsByAttribute(elements, atributte, destiny) {
    elements.sort(function (a, b) {
    var compA = $(a).attr(atributte);
    var compB = $(b).attr(atributte);
    return (compA compB) ? 1 : 0;
    });
    $.each(elements, function (idx, item) { destiny.append(item); });
    }

    You can use it with rows of tables, for example, with some personal tribute, for instance:

    sortElementsByAttribute($(“#myTable tbody”).children(“tr”).get(), “OrderAttrib”, $(“#myTable tbody”));

  2. A more generic function:

    function jQuerySort(elements, options) {
    if (typeof options === ‘undefined’) options = {};
    options.ascending = typeof options.ascending === ‘undefined’ ? 1 : (options.ascending ? 1 : -1);
    var list = elements.get();
    list.sort(function(a, b) {
    var compA = (options.find ? jQuery(a).find(options.find) : jQuery(a)).text().toUpperCase();
    var compB = (options.find ? jQuery(b).find(options.find) : jQuery(b)).text().toUpperCase();
    return options.ascending * ((compA compB) ? 1 : 0);
    });
    parent = elements.first().parent();
    jQuery.each(list, function(index, element) { parent.append(element); });
    }

  3. Old bump, but if you want case-sensitive sorting (IE: caps before lowers), simply remove .toUpperCase()

  4. This .get() at the end seems unnecessary:
    var listitems = mylist.children(‘li’).get();

    The jquery object is already an array so I believe this should work:
    var listitems = mylist.children(‘li’);

    And instead of appending items individually:
    $.each(listitems, function(idx, itm) { mylist.append(itm); });

    This seems to works also:
    mylist.append(lisitems);

  5. This worked great for sorting the definition terms in my list;
    however, do you know a way to keep the definitions (dd tags) associated with the terms?

    One would have a similar problem sorting the headings — say, all the H2’s — on a page, keeping that H2’s content associated with the heading.

    thanks for any advice.

  6. Hi, dan.

    I don’t understand why u use
    $.each(listitems, function(idx, itm) { mylist.append(itm); });
    if can use $(mylist).append(listitems);

  7. hey dan ,

    your script is one more reason to love jquery
    however i did come across some strange behaviour
    while playing with it
    i got
    <a href=”” rel=”nofollow”></a>
    <a href=”” rel=”nofollow”></a>
    <a href=”” rel=”nofollow”></a>
    <a href=”” rel=”nofollow”></a>

    and im setting the rel to different values every time and it works just fine as long as the numers are in the same decade e.g.

    <a href=”” rel=”nofollow”></a>
    <a href=”” rel=”nofollow”></a>
    <a href=”” rel=”nofollow”></a>
    <a href=”” rel=”nofollow”></a>

    the third one with rel=11 will be second instead of forth

    i hope my example is clear !
    any thoughts ?

    alex

  8. Hi guys, back again. I can’t believe this but I got it working perfectly. Now one remaining question. My issues was that I had specified the incorrect value where it says ‘span’ below:

    var compA = $(a).find(‘span’).text().toUpperCase();
    var compB = $(b).find(‘span’).text().toUpperCase();

    I changed it from ‘span’ to ‘a’ (for hyperlinks) and it worked!

    Now, my remaining issues is that my list 6 columns wide and I is displaying items alphabetically but in horizontal order, not vertical order. Is there any code I can append to it that can make it list the items in vertical order?

  9. Dear smart folks,

    I have been trying to get this working on a wordpress site but I am not having much luck.

    I have a submenu which consists of a list of links. Currently they get generated in order of date, but I just want to have them listed in alphabetical order. I thought maybe I can do this with jquery?

    The submenu div is called #projects-sub-menu

    I have put your code in my js file, and changed the first line to:

    var mylist = $(‘#projects-sub-menu’);

    I can’t get it working though, my list still shows up in order of date. I am a novice and would really appreciate any guidance.

  10. Hi Dan,

    thank you very much, Dan… I figured it out a few minutes ago by myself… I have to re-write a bit code to make it work the way it’s intended to run… but I can see clearly now the path to heaven.

    Thank you very much for helping me.

    And a brilliant solution, of course. Thx…
    Oliver

  11. @rossini – You need to run the sort within the $.get callback, after you append the items.

    $.get(url, function(d) {
      $(d).find(โ€˜entryโ€™).each(function() {
         //code to append items
       });
       //put sort code here
    });
    

    Remember that AJAX calls are asynchronous so you need to run the code at the right time – after it has finished. Currently it is just trying to sort an empty list

  12. Hi Dan, hi all,

    thank you so much for your support. I appreciate it very much.

    Okay, I get closer ๐Ÿ™‚

    It seems that when having a static UL in the HTML code, it gets sorted as intended.

    But I am clearing that static list and adding new ones on document ready, then trying to sort it…

    $(document).ready(function(){
    var adminrssfeed = “notesfromadmin.xml”;
    $(“#listticker”).empty();
    $.get(adminrssfeed, function(d) {
    $(d).find(‘entry’).each(function() {
    var $item = $(this);
    var description = $item.find(‘description’).text();
    var pubDate = $item.find(‘updated’).text();
    var mychildren = $(‘#listticker’).append(‘‘+description+’‘+pubDate+”);
    });
    });

    var mylist = $(“#listticker”);
    var listitems = mylist.children(‘li’).get();
    listitems.sort(function(a,b) {
    var compA = $(a).find(‘span’).text().toUpperCase();
    var compB = $(b).find(‘span’).text().toUpperCase();
    return (compA compB) ? 1 : 0;
    });
    $.each(listitems, function(idx, itm) { mylist.append(itm); });

    };

    This fills the listticker ul element with new entries from an xml file, but the sorting part doesn’t work. I think, this is because I am appending new elements, but they are not there yet when I try to sort them.

    I can send the full code including the xml file and send that to you if you like.

    Is there something that I can do after appending the elements and before sorting them, to make them sortable? ๐Ÿ™‚

    Or am I missing something here?

    Thank you in advance,

    Oliver

  13. Hi rossini. Sorry, I am rather busy at the moment. I find your mixture of jQuery and traditional javascript confuses things uneccessarily, it is much easier to assemble elements with jQuery:

    var mylist = $(‘#listticker’).append(‘<li><img src=”img/me.png” /><a href=”” class=”blub”>’+description+'</a><span class=”bla”>’+strDateTime+'</span></li>’);

    There are a couple of things wrong with your sorting javascript. Firstly, you are missing a semi-colon after the sort function and before the each statement. Secondly your comparison logic (the return statement) is not correct. This works:

    $(function() {
    var mylist = $(‘#listticker’);
    var listitems = mylist.children(‘li’).get();
    listitems.sort(function(a,b) {
    var compA = $(a).find(‘span’).text().toUpperCase();
    var compB = $(b).find(‘span’).text().toUpperCase();
    return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
    });
    $.each(listitems, function(idx, itm) { mylist.append(itm); });
    });

    See this demo at JSFiddle

    Hope that helps

  14. Dan? Any assistance or hint that you (or anybody) could give? I’d be happy to send you the full source code, if you would like to see it…

    I know that you probably are involved in many projects, but maybe you want to help a poor dumbhead with his code…?!

    Thx in advance,
    Oliver

  15. I seem to be too dumb to use it.

    I am setting up dynamic content for a UL with the id “listticker”.

    code snippet

    for a number of description/strDateTime variables, I am doing this …

    var neuLI = document.createElement(“li”);
    var neuIMG = document.createElement(“IMG”);
    neuIMG.src = “img/me.png”;
    var neuA = document.createElement(“A”);
    neuA.href = “”;
    neuA.className = “blub”;
    sometext = document.createTextNode(description);
    neuA.appendChild(sometext);
    var neuSPAN = document.createElement(“span”);
    neuSPAN.className = “bla”;
    neuSPAN.innerHTML = strDateTime + “”;
    neuLI.appendChild(neuIMG);
    neuLI.appendChild(neuA);
    neuLI.appendChild(neuSPAN);
    document.getElementById(“listticker”).appendChild(neuLI);

    which should “translate” into the following html code:

    <a HREF=”” rel=”nofollow”>first DESCRIPTION</A>
    first Date and Time
    .
    …further list elements…
    .

    However, I need the UL with ID of “listticker” being sorted by the content of the “span” elements (ie. by date and time).

    I tried to use the following code within the $(document).ready function…

    var mylist = $(“#listticker”);
    var listitems = mylist.children(‘li’).get();
    listitems.sort(function(a, b) {
    top.alert(“I am running”);
    var compA = $(a).find(‘span’).text().toUpperCase();
    var compB = $(b).find(‘span’).text().toUpperCase();
    return (compA compB) ? 1 : 0;
    })
    $.each(listitems, function(idx, itm) { mylist.append(itm); });

    I inserted the top.alert statement, but it does not get called…
    And the list does not get sorted, too. ๐Ÿ™‚

    Shall I add further commands?

    BTW, I am using jquery 1.2.6 …

    Is there some working demo available?

    Thank you in advance, dear jquery experts….

    Oliver

  16. I ran into some complications with this. When I identified the list by class (e.g. $(‘ul.listylist’)) it had a problem where it doubled the list elements every time it was sorted after the first time. However, when I changed it to use an ID (e.g. $(‘#mylistylist’)) it worked just fine.

  17. There is a way to speed it up, by reducing amount of append calls to just one at the very end of the sorting process.

    btw. if anyone would like to get desc sorting just change return line as below:
    return (compA > compB) ? -1 : (compA < compB) ? 1 : 0; Great script Dan!

  18. @kshitiz I am not sure there is much that can speed things up when you are dealing with large quantities of elements like that. You best approach would be to re-think the page so that you are not displaying so many results or to do the sorting on the server rather than client.

  19. Hi, I have used your script to make a travel website. but, while doing the sorting of hotel by name system slows down. Script works fine with 20 – 80 hotels. But when list is bigger than 300 hotel it hangs the PC.

    I am using your script to sort divs.

    here is the code

    please help me to speed this up.

    var mylist = $(‘#hiddenresult’);
    var listitems = mylist.children(‘div.result’).get();
    listitems.sort(function(a, b) {
    var compA = $(a).find(“#h_hotelnameandprice”).text().toUpperCase();
    var compB = $(b).find(“#h_hotelnameandprice”).text().toUpperCase();
    return (compA compB) ? 1 : 0;
    })
    $.each(listitems, function(idx, itm) {
    mylist.append(itm);
    });

  20. @mark Yes, the array.sort function allows you to sort by anything, just use jQuery to select the information you want to sort by, then compare and return -1 when “a” is to be a lower index than “b”, 0 when they are the same and 1 when “b” is to be a lower index than “a”. For more information, google for ‘javascript array sort’.

  21. ups! I realized just now that i did it’s not the same as you proposse (i’ve just copied the return line). Now in jquery 1.3.2 is posible do something like this:

    var listitems = $(‘ul li’);
    listitems.sort(function(a, b) {
    var compA = $(a).text().toUpperCase();
    var compB = $(b).text().toUpperCase();
    return (compA compB) ? 1 : 0;
    })

  22. Thank you for your code, it helps me a lot! It’s important to note that the sort method in jquery opbjects only works in version 1.3.2, (and above when released i suppose). I was developing in 1.3.1 and gets an error.

    Thanks again and sorry if english is not good!

  23. @Richstaats You were nearly there. It should be something like:

    var compA = $(a).find(‘span.abcโ€™).text().toUpperCase();
    var compB = $(b).find(‘span.abcโ€™).text().toUpperCase();

    the compA and compB is the text you are comparing, so with the above statement I am retrieving the text within the span tag.

    Hope that helps,
    Dan

  24. im sorry i had a typo:

    I added a span class to each of the list items and gave it a class of โ€œabcโ€

    should read:

    I added a span class to each of the last names within the list items and gave it a class of โ€œabcโ€

    thanks!

  25. Hey Dan, Thank you so much for this, you made it look so easy. I am trying to take this one step further, if possible. I have a list of names (first name and last name) and would like to have this function sort by last name, without forcing the list to be echoed: {lastname}, {firstname}. ie, Staats, Rich. It would be nice to echo: Rich Staats yet sort by “Staats” instead of “Rich.”

    I attempted my own solution based on the attr comment above. I added a span class to each of the list items and gave it a class of “abc”. I then attempted to sort like so:

    var compA = $(a).attr(‘span.abc’).toUpperCase();
    var compB = $(b).attr(‘span.abc’).toUpperCase();

    That however did not work. Can you possibly point me in the right direction?

    Rich

  26. Ups, my bad! I was having a problem with the selector, that was all, script works like a charm!

    Thank you!

  27. Quick question: I tried your script and it works fine the first time, but if I run it twice (I have a link that orders by price and other by category), don’t know why but it duplicates the result!

    What can I do to avoid this?

    Thanks a lot!

  28. Thanks, worked perfectly. Its seemingly quite simple but I couldn’t work out myself how to do it.

  29. The array.sort function allows you to sort by anything. In my example I am retrieving the text of the element with $(a).text() but I could easily retrieve an attribute and compare that instead $(a).attr(‘id’) etc.

  30. These orders alphabetically nicely, but what would i need to do to make it sort by say class or attribute etc instead of alphabetically..

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.