Saturday, June 14, 2008

Moving select options with jQuery

I wanted to move select options up/down on a click of a button. The following jQuery snippet does exactly that, if you want to put in some moveby variable (delta) execute this stuff within a for loop

#UPDATE My implementation was valid ages & jQueries ago, thanks to imants.horsts for updated solution

$.fn.moveSelectedUp = function() {
var selectedOptions = $(this).selectedOptions();
var prev = $(selectedOptions).first().prev();
$(selectedOptions).insertBefore(prev);
}

$.fn.moveSelectedTop = function() {
var selectedOptions = $(this).selectedOptions();
var first = $(this).children("option").not(":selected").first();
$(selectedOptions).insertBefore(first);
}

$.fn.moveSelectedBottom = function() {
var selectedOptions = $(this).selectedOptions();
var last = $(this).children("option").not(":selected").last();
$(selectedOptions).insertAfter(last);
}


This solution is tested in FF3 & IE6/7, should work elsewhere as well

11 comments:

Lorenzo said...

It doesn't seem to work correctly with multiple-choice select.

Daniele said...

try this:
function upfield() {
var el = $('#selectedfield').children('[@selected]:first').prev();
$("#selectedfield")
.children('[@selected]')
.each(function(){
$(this)
.insertBefore(el);
});

}

function downfield() {
var el = $('#selectedfield').children('[@selected]:last').next();
$("#selectedfield")
.children('[@selected]')
.each(function(){
$(this)
.insertAfter(el);
el = $(el).next();
});
}

Alex said...

function moveUpItem(){
$('#listID option:selected').each(function(){
$(this).insertBefore($(this).prev());
});
}

function moveDownItem(){
$('#listID option:selected').each(function(){
$(this).insertAfter($(this).next());
});

}

Diego Arbelaez said...

in case anyone is still looking for this... created 4 links with an attribute of _move with values ('top', 'up', 'down', 'bottom')


$(document).ready( function() {

$('a[_move]').click(function(){
var move = $(this).attr('_move');

$('#rw_columns_order option:selected').each(function(){

switch(move)
{
case 'up':
$(this).insertBefore($(this).prev());
break;

case 'down':
$(this).insertAfter($(this).next());
break;

case 'top':
if($(this).attr('index') > $('#rw_columns_order option:first').attr('index'))//this check is to prevent trying to move item that is already at the top
{
$(this).insertBefore($('#rw_columns_order option:first'));
}
break;

case 'bottom':
if($(this).attr('index') < $('#rw_columns_order option:first').attr('index'))//this check is to prevent trying to move item that is already at the bottom
{
$(this).insertAfter($('#rw_columns_order option:last'));
}
break;
}

});

return false;
});


});

Anonymous said...

Diego arbelez, nice code. I used it. But there is a bug in it. For the bottom case you check the following:

if($(this).attr('index') < $('#selector option:first').attr('index'))

but you should compare it to option:last.

Otherwise, good code and thanks for helping me out.

Unknown said...

This works nicely for me:

$.fn.selectedOptions = function() {
return $(this).children('option:selected');
}

$.fn.moveSelectedDown = function() {
var selectedOptions = $(this).selectedOptions();
var next = $(selectedOptions).last().next();
$(selectedOptions).insertAfter(next);
}
$.fn.moveSelectedDown = function() {
var selectedOptions = $(this).selectedOptions();
var next = $(selectedOptions).last().next();
$(selectedOptions).insertAfter(next);
}

Daniele said...

tims: very elegant
you miss moveSelectedUp with
var prev = $(selectedOptions).first().prev().prev();
$(selectedOptions).insertAfter(prev);

DarkSide said...
This comment has been removed by the author.
DarkSide said...

This if how moveSelectedUp should look like to work perfectly:

$.fn.moveSelectedUp = function() {
var selectedOptions = $(this).selectedOptions();
var prev = $(selectedOptions).first().prev();
$(selectedOptions).insertBefore(prev);
}

DarkSide said...

And this is how move to top and move to bottom functions should look like:

$.fn.moveSelectedTop = function() {
var selectedOptions = $(this).selectedOptions();
var first = $(this).children("option").not(":selected").first();
$(selectedOptions).insertBefore(first);
}

$.fn.moveSelectedBottom = function() {
var selectedOptions = $(this).selectedOptions();
var last = $(this).children("option").not(":selected").last();
$(selectedOptions).insertAfter(last);
}

pentarim said...

@imants.horsts thanks, updated