Skip to content

Instantly share code, notes, and snippets.

@pacoguzman
Created December 11, 2010 11:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pacoguzman/737324 to your computer and use it in GitHub Desktop.
Save pacoguzman/737324 to your computer and use it in GitHub Desktop.
Transform select multiple to a single select - Progressive Enhancement
// Convert a multi-select into a single_select which keeps track of choices in an external list with
// hidden fields. The hidden fields have the same field name as the multi select. list_class
// defaults to the select id + "_data" Chosen options are moved from the 'Add' option group to
// the 'Remove' option group, Each suffixed by the given 'title' or the string "Option". The select
// header is extracted from the select's label for - if it exists. Otherwise it just prints 'Options...'
//
// Example:
//
// $("select_multi").multiSingleSelect({list_class:'select_multi_data', title:'Option',
// remove: "Remove", add: "Add", prompt: "Options..."});
//
// Dependencies: prototype-1.7.0
// insert(insertion) == insert({bottom:insertion})
(function() {
var MultiSingle = {
selectRemove : function(elem_id, list_class, html, val) {
var element = $(elem_id);
var selected = element.down("optgroup.remove option[value='" + val + "']");
if (selected) {
selected.writeAttribute('selected', false).removeClassName('removable');
this.addOrdered(elem_id, val, selected);
}
element.next("ul." + list_class).select("li").each(function(li) {
if (li.down('span').innerHTML == html) {
li.remove();
}
});
element.next("span." + list_class).select("input").each(function(input) {
if (input.getValue() == val) {
input.remove();
}
});
},
addOrdered : function(elem_id, val, selected) {
var add_group = $(elem_id).down("optgroup.add");
var added_options = add_group.select("option");
if (added_options.length > 0) {
var selected_position = selected.retrieve("position");
var option_detected = added_options.detect(function(option) {
return option.retrieve("position") > selected_position;
});
if (option_detected) {
return option_detected.insert({before: selected});
}
}
add_group.insert(selected);
},
selectAdd : function(element, selected, list_class) {
element = $(element);
var html = selected.innerHTML;
var val = selected.readAttribute("value");
var li = new Element('li').store('html', html).store('val', val).insert("<span>" + html + "</span><a href\"\">x</a>");
element.next("ul." + list_class).insert(li);
element.next("span." + list_class).insert(new Element('input', {type: 'hidden', name: element.retrieve("name"), value: val}));
}
};
Element.addMethods('SELECT', {
multiSingleSelect: function(element, options) {
element = $(element);
var elem_id = element.readAttribute("id");
var settings = Object.extend({
list_class: elem_id + "_data",
title: "Option",
add: "Add",
remove: "Remove"
}, options || {});
if (!settings.prompt) {
var elem_label = $$("label[for=" + elem_id + "]");
if (elem_label.size() > 0) {
settings.prompt = elem_label.first().innerHTML + "...";
} else {
settings.prompt = settings.title + "s" + "...";
}
}
// Grab the name of the select and store, and give that name to all hidden fields
var elem_name = element.readAttribute('name');
element.store('name', elem_name).writeAttribute('name', false);
var add_group = new Element("optgroup", {'class': 'add', label: settings.add + ' ' + settings.title});
var remove_group = new Element("optgroup", {'class': 'remove', label: settings.remove + ' ' + settings.title});
element.insert(add_group);
element.insert(remove_group);
element.insert({after: new Element("ul", {'class': settings.list_class})});
element.insert({after: new Element("span", {'class': settings.list_class})});
var selectedAdd = function($elem, $selected) {
$selected.writeAttribute('selected', false).addClassName('removable');
remove_group.insert($selected);
MultiSingle.selectAdd($elem, $selected, settings.list_class);
};
// remove each selected and push to end
element.select('option').each(function(selected, i) {
selected.store("position", i + 1);
if (!selected.readAttribute('selected')) {
add_group.insert(selected);
} else {
selectedAdd(element, selected);
}
});
element.insert({top: new Element("option", {'class': 'directive', value: '0'}).update(settings.prompt)});
element.observe('change', function(event) {
var selected = $(this).down(":selected");
if (!selected || selected.readAttribute('value') == "0") return;
if (selected.hasClassName('removable')) {
MultiSingle.selectRemove(elem_id, settings.list_class,
selected.innerHTML, selected.readAttribute('value'));
} else {
selectedAdd(this, selected);
}
this.selectedIndex = 0;
});
document.on('click', 'ul.' + settings.list_class + " a", function(event, link) {
event.preventDefault();
var li = link.up("li");
MultiSingle.selectRemove(elem_id, settings.list_class, li.retrieve('html'), li.retrieve('val'));
});
return element.writeAttribute('multiple', false).writeAttribute('size', false);
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment