Move list item to top of unordered list using jQuery

Solution 1:

Found this even neater:

$('li').on('click', function() {
    $(this).parent().prepend(this);
});​

Live example

Solution 2:

I came up with a solution that seems to work pretty well. It's a proof of concept, so you'll probably have to modify it a bit to work better for your specific case. Also, I only tested it in Firefox, but I don't see any reason why this wouldn't work in all the browsers. Anyway, here it is:

<script type="text/javascript">
  $(document).ready(function() {
    $('li').click(function() {
      // the clicked LI
      var clicked = $(this);

      // all the LIs above the clicked one
      var previousAll = clicked.prevAll();

      // only proceed if it's not already on top (no previous siblings)
      if(previousAll.length > 0) {
        // top LI
        var top = $(previousAll[previousAll.length - 1]);

        // immediately previous LI
        var previous = $(previousAll[0]);

        // how far up do we need to move the clicked LI?
        var moveUp = clicked.attr('offsetTop') - top.attr('offsetTop');

        // how far down do we need to move the previous siblings?
        var moveDown = (clicked.offset().top + clicked.outerHeight()) - (previous.offset().top + previous.outerHeight());

        // let's move stuff
        clicked.css('position', 'relative');
        previousAll.css('position', 'relative');
        clicked.animate({'top': -moveUp});
        previousAll.animate({'top': moveDown}, {complete: function() {
          // rearrange the DOM and restore positioning when we're done moving
          clicked.parent().prepend(clicked);
          clicked.css({'position': 'static', 'top': 0});
          previousAll.css({'position': 'static', 'top': 0}); 
        }});
      }
    });
  });
</script>

<ul>
 <li><a>Hank</a></li>
 <li><a>Alice</a></li>
 <li><a>Tom</a></li>
 <li><a>Ashlee</a></li>
</ul>

It calculates the difference in offsets between the clicked LI and first LI and moves the clicked one up to the top by setting its position to relative and animating the top property. Similarly, it calculates how much space was left behind by the clicked LI and moves all the previous ones down accordingly. When it's done with the animations, it rearranges the DOM to match the new order and restores the positioning styles.

Hope that helps!

Solution 3:

Assuming:

<ul id="list">
 <li><a>Hank</a></li>
 <li><a>Alice</a></li>
 <li><a>Tom</a></li>
 <li><a>Ashlee</a></li>
</ul>

then:

$("#list a").click(function() {
  $(this).parent().before("#list a:first");
  return false;
});

If you want to animate then it's a little harder. One option:

$("#list a").click(function() {
  $(this).parent().slideUp(500).before("#list a:first").slideDown(500);
  return false;
});

Another option:

$("#list a").click(function() {
  var item = $(this).parent();
  var prev = item.prev();
  while (prev.length > 0) {
    item.before(prev);
    prev = item.prev();
  }
  return false;
});

but I doubt you'll get smooth animation that way.

Solution 4:

i played around with the fiddle No Surprises has made, and extended the code for swapping two arbitrary sets of elements (the only restriction being they must directly follow each other).

see here: http://jsfiddle.net/ZXYZ3/139/

Solution 5:

I came up with this solution: http://jsfiddle.net/FabienDemangeat/TBYWw/

The idea is to choose the index of the Li element which will move and its destination. If the destination value is inferior to the index of the li element to move, the effect will be reversed.

Some parts are not perfect but it can be a start point. I inspired myself from the snippet provided by "No Surprises"

The main function swapLiElements swaps two li elements and the callback function as parameters allows to do more than one swap easily (see fiddle).

function swapLiElements($northLi, $southLi, isPushingDown, duration, easing, callbackFunction) {

    var movement = $northLi.outerHeight();

    // Set position of the li elements to relative
    $northLi.css('position', 'relative');
    $southLi.css('position', 'relative');

    // Set the z-index of the moved item to 999 to it appears on top of the other elements
    if(isPushingDown)
        $northLi.css('z-index', '999');
    else        
        $southLi.css('z-index', '999');

    // Move down the first li
    $northLi.animate({'top': movement}, {
        duration: duration,
        queue: false,
        easing: easing,
        complete: function() {
            // Swap the li in the DOM
            if(isPushingDown)
                $northLi.insertAfter($southLi);
            else
                $southLi.insertBefore($northLi);

            resetLiCssPosition($northLi);
            resetLiCssPosition($southLi);

            callbackFunction();
        }
    });

    $southLi.animate({'top': -movement}, {
        duration: duration,
        queue: false,
        easing: easing,
    });

}