Actually, I've just gotten it working. It's amazing what a dinner
break can do for a stalled mind.

As it turns out, I do, in fact, need to be able to sort the deeper
list items. Luckily, a requirement is that they *not* be moved into a
new list.

I forgot to mention earlier that I'd tried using index() with the same
bad results. In any case, since I now have no need to limit this to
the top level, my selector has become much simpler. Using index()
works. Although, I think that I might have had an error somewhere
else.

So, in case someone else stumbles upon this and it's useful, this is
what I ended up with:

/* add drag handles to each section list item
 */
$('#section_list li').each(function()
{
        $(this).prepend('<a href="#" class="Handle"></a>');
});

var start_pos;

$('#section_list ul').sortable({
        items: 'li',
        axis: 'y',
        containment: 'parent',
        delay: 0,
        distance: 4,
        helper: dragHelp,
        forceHelperSize: true,
        forcePlaceholderSize: true,
        handle: 'a.Handle',
        tolerance: 'pointer',
        cursor: 'move',
        opacity: 0.6,
        zIndex: 200,
        
        start: function(event, ui)
        {
                start_pos = 
$(ui.item).parent('ul').children('li').index($(ui.item));
        },
        update: function(event, ui)
        {
                /* element ID is of the form 'section_n'
                 */
                var item_id = 
$(ui.item).attr('id').substring('section_'.length);

                /* get end position
                 */
                var end_pos = 
$(ui.item).parent('ul').children('li').index($(ui.item));
                
                /* number places moved
                 */
                var delta = Math.abs(end_pos - start_pos);
                var direction = (start_pos < end_pos) ? 'down' : 'up';
                
                showIndicator('#section_content', {top: 100});

                $.ajax({
                        url: 
'/admin/sections/move/'+direction+'/'+item_id+'/'+delta,
                        cache: false,
                        timeout: 10000,
                        success:
                                function(text)
                                {
                                        hideIndicator();
                                        alert(text);
                                },
                        error:
                                function (XMLHttpRequest, textStatus, 
errorThrown)
                                {
                                        alert(textStatus);
                                }
                });
        }
});

The 10 sec timeout is because I still need to tidy things up server
side. It's working, though it's ridiculously slow.

I had a spot of trouble getting the end position. I'd have thought
that this should do it:

$(ui.item).siblings('li:not(li ul li)').andSelf().index($(ui.item));

But I got consistently bad numbers again. I couldn't figure out how to
get only the siblings within the same UL (and the item being moved)
except to do:

$(ui.item).parent('ul').children('li').index($(ui.item))

I'll bet there's a better way than that.

Sorry for not providing a link to explain MPTT. It stands for Modified
Preorder Tree Traversal. It's actually much easier to grasp than the
name suggests. You can read a pretty good overview here:

http://www.sitepoint.com/article/hierarchical-data-database/

Here's a more in-depth article:
http://dev.mysql.com/tech-resources/articles/hierarchical-data.html

The short version is that each record has parent_id, left, and right
fields (usually lft & rght). This allows for a tree hierarchy.

I'd been thinking that I'd need to pass the parent_id, lft, and rght
values in addition to the id. Meaning that I'd also need to store
those somehow when the page was first rendered, perhaps in hidden
spans or something similar (I wouldn't want to pollute the DOM). But,
it turns out that I can simply pass the number of places to move by,
as well as the direction.

Next, I'll have to figure out how to re-order across 2 lists, if only
to satisfy my own curiosity (or the ever-changing whims of the
client).

On Thu, Jun 11, 2009 at 10:21 PM, Charlie<charlie...@gmail.com> wrote:
> one little addition to markup add an id ="list" to ul due to $sortable
> requirements
>
>          $("#section_list li:not('li ul li')").each( function(i) {
>
>             position= i+1
>               myId="foo_"+position
>
>             $(this).attr("id",myId);
>               
>
>         });
>
>         $("#list").sortable();
>
>
>
>         $("button").click(function() {
>
>         var result = $('#list').sortable('toArray');// this returns id's in
> order they appear after sort, parse out the foo_
>
>         alert(result);
>
>         });
>
> i don't understand the MPTT stuff, or implications but hope this helps
>
>
> brian wrote:
>
> Given the following structure:
>
> <div id="section_list">
>       <ul id="list">
>               <li></li>
>               <li>
>                       <ul>
>                               <li></li>
>                       </ul>
>               </li>
>               <li>
>                       <ul>
>                               <li></li>
>                       </ul>
>               </li>
>       </ul>
> </div>
>
> How should I select just the top-level list items? I thought
> '#section_list > ul > li' should do it but it doesn't appear to be so.
> At least, the code I've got is giving me strange results. I'd like to
> confirm whether my selector is good or not.
>
> What I'm trying to do is get the position of a list item (only looking
> at the top level). I'm using sortable and need to be able to get the
> start and end positions of the item so i can update the database. What
> I've come up with so far is to call this function from sortable's
> start callback. It seems like an ugly way to go about this but I'm a
> bit stumped as to a better way.
>
> function getStartPosition(id)
> {
>       var position = 0;
>       $('#section_list > ul > li').each(function(indx, el)
>       {
>               if ($(el).attr('id') == id) { position = indx + 1; }
>       });
>       return position;
> }
>
> Not only does it look hacky, but it's giving me obviously bad results.
>
> To complicate things further, I'm using MPTT for my tree data, so
> sortable's serialize isn't much help, unfortunately. I originally
> thought I might figure out the parent_id, lft, and rght values upon
> sort but that would entail storing all that data somewhere in the DOM.
> I then realized that, if I can just get the number of positions moved,
> I could update the DB with that information.
>
> All this to say that, if someone else has tackled this, I'd really
> appreciate some pointers.
>
>
>

Reply via email to