Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort li elements without ruining the sub-list

I am trying to write some sorting code. I would like to sort a list like:

  • banana
  • cake
    • egg
    • doughnut
    • burger
  • rice
  • apple

and turn into:

  • apple
  • banana
  • cake
    • egg
    • doughnut
    • burger
  • rice
<html>
    <head>
        <title>Sort list items alphabetically with Javascript</title>
        <script type="text/javascript">

        function sortUnorderedList(ul, sortDescending) {
          if(typeof ul == "string")
            ul = document.getElementById(ul);

          var lis = ul.getElementsByTagName("LI");
          var vals = [];

          for(var i = 0, l = lis.length; i < l; i++)
            vals.push(lis[i].innerHTML);

          vals.sort();

          if(sortDescending)
            vals.reverse();

          for(var i = 0, l = lis.length; i < l; i++)
            lis[i].innerHTML = vals[i];
        }

        window.onload = function() {
          var desc = false;
          document.getElementById("test").onclick = function() {
            sortUnorderedList("list", desc);
            desc = !desc;
            return false;
          }
        }

        </script>
    </head>
    <body>
        <input type="button" id="test" value="Sort List (click again to reverse)"/>
        <ul id="list">
            <li>banana</li>
            <li>cake</li>
              <ul>
                <li>egg</li>
                <li>doughnut</li>
                <li>burger</li>
              </ul>
            <li>rice</li>
            <li>apple</li>
        </ul>
    </body>
</html>
like image 608
Renz Avatar asked Oct 26 '25 06:10

Renz


1 Answers

The main issue with your code is that your HTML is invalid; ul cannot be a child of another ul. Only li or ol can. This invalid HTML causes the li to be rendered differently than you expect so the sorting is affected. To fix this correct the HTML so the nested 'cake' ul is within the li.

As you've tagged the question with jQuery, here's a much simple implementation of what you originally had:

jQuery($ => {
  let $ul = $('#list');

  $('#test').on('click', function() {
    let sort = $(this).data('sortasc');
    let dir = sort ? [1, -1] : [-1, 1];
    $ul.children('li').sort((a, b) => a.innerText.trim() < b.innerText.trim() ? dir[0] : dir[1]).appendTo($ul);
    $(this).data('sortasc', !sort);
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="test">Toggle sort</button>
<ul id="list">
  <li>banana</li>
  <li>
    cake
    <ul>
      <li>egg</li>
      <li>doughnut</li>
      <li>burger</li>
    </ul>
  </li>
  <li>rice</li>
  <li>apple</li>
</ul>
like image 116
Rory McCrossan Avatar answered Oct 28 '25 21:10

Rory McCrossan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!