tags:

views:

984

answers:

3

Lets say i have the following unordered list

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

What im looking for is when i click on Tom, that it moves (animated and without dragging) to the top of the list (index 0).

Ive considered jquery sortable, but i cant find a way to activate the moving part programmatically.

+2  A: 

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.

cletus
Not exactly what im looking for but i guess thats the next best thing. Voted up.
Fabian
A: 

I doubt if you can animate li items in javascript..

You would need to create a div, copy the contents of the li to the div and then move the div, and then reinsert the li..

Mongus Pong
+1  A: 

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!

No Surprises
Wow that looks very intresting, im definitely gonna give this a go. Voted up!
Fabian
Is there a version that moves li items down one?
crosenblum