Renz
Renz

Reputation: 43

Sort li elements without ruining the sub-list

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

and turn into:

<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>

Upvotes: 4

Views: 163

Answers (1)

Rory McCrossan
Rory McCrossan

Reputation: 337560

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>

Upvotes: 3

Related Questions