3

I have a listbox which has two buttons to control the priority of the contained items. Everything is working except for the move down logic when multiple items are selected.

If two items are selected, nothing happens, and if three or more are selected, only the bottom one moves.. is there is a simple way to handle this using jQuery?

Current implementation:

<div id="selected">
    <div><strong>Selected</strong></div>
    <div id="selected-items">
        <input type="select" multiple="multiple" id="selected-items-select">
            <option/>
        </input> 
    </div>
</div>

<div id="priority" style="display: none;">
    <div><input type="button" id="move-up" value="^" /></div>
    <div><input type="button" id="move-down" value="v" /></div>
</div>

<script type="text/javascript">

    $(document).ready(function () {
        $('#move-up').click(moveUp);
        $('#move-down').click(moveDown);
    });

    function moveUp() {
        $('#selected-items select :selected').each(function (i, selected) {
            $(this).insertBefore($(this).prev());
        });
        $('#selected-items select').focus().blur();
    }

    function moveDown() {
        $('#selected-items select :selected').each(function (i, selected) {
            $(this).insertAfter($(this).next());
        });
        $('#selected-items select').focus().blur();
    }
</script>

Also, notice the $('#selected-items select').focus().blur(); line.. I have it because sometimes the priorities don't update until the listbox gets focus. Is there a cleaner way to do this too?

shuniar
  • 2,592
  • 6
  • 31
  • 38

1 Answers1

8

When you try to move down multiple items, you're looping through them in order from top to bottom. Unfortunately, that means you're moving the first selected item after the second selected item, and then the second selected item after the first, resulting in no change.

I solved the problem by using the trick from here to loop through the selection backwards when moving down.

UPDATE: added a test to see if you've moved your selection to the top or bottom to avoid resorting the selection: http://jsfiddle.net/FBTVk/1/

function moveUp() {
    $('#selected-items select :selected').each(function(i, selected) {
        if (!$(this).prev().length) return false;
        $(this).insertBefore($(this).prev());
    });
    $('#selected-items select').focus().blur();
}

function moveDown() {
    $($('#selected-items select :selected').get().reverse()).each(function(i, selected) {
        if (!$(this).next().length) return false;
        $(this).insertAfter($(this).next());
    });
    $('#selected-items select').focus().blur();
}
Community
  • 1
  • 1
Blazemonger
  • 90,923
  • 26
  • 142
  • 180
  • Nice work - still a little strange when multiple reach end cases though. At top/bottom they will start switching amongst themselves again. May want to put in special case to catch when at select list boundary. – mrtsherman Oct 03 '11 at 17:23
  • Awesome, this works great. I knew what the problem was but was unsure how to loop backwards. – shuniar Oct 03 '11 at 17:31
  • @shuniar: note that the SO question I linked to has multiple solutions for reversing a selection. Read it and choose the one you like best. – Blazemonger Oct 03 '11 at 17:33
  • @mrtsherman how would I go about adding a case to catch the boundary? I know how I would do it on the serverside but I am still trying to learn best practices for jQuery. – shuniar Oct 03 '11 at 17:35
  • @shuniar: I already added that to my code. It tests if you're at the boundary and exits the `.each` with a `return false`. – Blazemonger Oct 03 '11 at 17:36
  • @mblase75 thanks I didn't see that before I posted my comment. – shuniar Oct 03 '11 at 17:37