"Double List Box From Select"
Bootstrap 3.3.0 Snippet by fractorr

<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css"> <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script> <script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <!------ Include the above in your HEAD tag ----------> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>The HTML5 Herald</title> <meta name="description" content="The HTML5 Herald"> <meta name="author" content="SitePoint"> <link rel="stylesheet" href="css/styles.css?v=1.0"> <!--[if lt IE 9]> <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> </head> <body> <select id="dual-list-options" class="crossselectbox" name="dual-list-options[]" multiple="multiple" size="10"> <option value="1" selected="selected">Option 1</option> <option value="2" selected="selected">Option 2</option> <option value="3" selected="selected">Option 3</option> <option value="4" selected="selected">Option 4</option> <option value="5" selected="selected">Option 5</option> </select> <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script> </body> </html>
.dual-list .list-group { margin-top: 8px; } .list-left li, .list-right li { cursor: pointer; } .list-arrows button { margin-bottom: 20px; } .left-check-col { padding-left: 0px; }
(function($) { $.widget("ui.crossselectbox", { options: { sortable: true, searchable: true, doubleClickable: true, animated: 'fast', show: 'slideDown', hide: 'slideUp', dividerLocation: 0.6, availableFirst: false, nodeComparator: function(node1,node2) { var text1 = node1.text(), text2 = node2.text(); return text1 == text2 ? 0 : (text1 < text2 ? -1 : 1); }, container: '<div class="container"><div class="row"></div></div>', selectedContainer: '<div class="selected well text-right"></div>', availableContainer: '<div class="available well text-right"></div>', selectedActions: '<div class="row"><div class="col-md-12 form-group"><div class="input-group"><span class="input-group-addon glyphicon glyphicon-search" style="top: 0px;"></span> <input type="text" name="SearchDualList" class="form-control" placeholder="search"> <span class="input-group-addon glyphicon glyphicon-unchecked selector" style="cursor: pointer; top: 0px;" title="Select All"></span><span class="input-group-addon glyphicon glyphicon-minus move-right" style="cursor: pointer; top: 0px;" title="Remove Selected"></span></div></div></div>', availableActions: '<div class="row"><div class="col-md-12 form-group"><div class="input-group"><span class="input-group-addon glyphicon glyphicon-search" style="top: 0px;"></span> <input type="text" name="SearchDualList" class="form-control" placeholder="search"> <span class="input-group-addon glyphicon glyphicon-unchecked selector" style="cursor: pointer; top: 0px;" title="Select All"></span><span class="input-group-addon glyphicon glyphicon-minus move-left" style="cursor: pointer; top: 0px;" title="Remove Selected"></span></div></div></div>', selectedList: '<ul class="list-group" id="dual-list-left">', availableList: '<ul class="list-group" id="dual-list-right">' }, _create: function() { this.element.hide(); this.id = this.element.attr("id"); this.container = $('<div class="ui-multiselect ui-helper-clearfix ui-widget"></div>').insertAfter(this.element); this.count = 0; // number of currently selected options this.selectedContainer = $('<div class="selected"></div>').appendTo(this.container); this.availableContainer = $('<div class="available"></div>')[this.options.availableFirst?'prependTo': 'appendTo'](this.container); this.selectedActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><span class="count">0 '+$.ui.multiselect.locale.itemsCount+'</span><a href="#" class="remove-all">'+$.ui.multiselect.locale.removeAll+'</a></div>').appendTo(this.selectedContainer); this.availableActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><input type="text" class="search empty ui-widget-content ui-corner-all"/><a href="#" class="add-all">'+$.ui.multiselect.locale.addAll+'</a></div>').appendTo(this.availableContainer); this.selectedList = $(this.options.selectedList).bind('selectstart', function(){return false;}).appendTo(this.selectedContainer); this.availableList = $(this.options.availableList).bind('selectstart', function(){return false;}).appendTo(this.availableContainer); this.container = $(this.options.container).insertAfter(this.element); this.selectedContainer = $(this.options.selectedContainer).appendTo(this.container); this.availableContainer = $(this.options.availableContainer).appendTo(this.container); this.selectedActions = $(selectedActions).appendTo(this.selectedContainer); this.availableActions = $(availableActions).appendTo(this.availableContainer); this.selectedList = $('<ul class="selected connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.selectedContainer); this.availableList = $('<ul class="available connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.availableContainer); var that = this; // set dimensions this.container.width(this.element.width()+1); this.selectedContainer.width(Math.floor(this.element.width()*this.options.dividerLocation)); this.availableContainer.width(Math.floor(this.element.width()*(1-this.options.dividerLocation))); // fix list height to match <option> depending on their individual header's heights this.selectedList.height(Math.max(this.element.height()-this.selectedActions.height(),1)); this.availableList.height(Math.max(this.element.height()-this.availableActions.height(),1)); if ( !this.options.animated ) { this.options.show = 'show'; this.options.hide = 'hide'; } // init lists this._populateLists(this.element.find('option')); // make selection sortable if (this.options.sortable) { this.selectedList.sortable({ placeholder: 'ui-state-highlight', axis: 'y', update: function(event, ui) { // apply the new sort order to the original selectbox that.selectedList.find('li').each(function() { if ($(this).data('optionLink')) $(this).data('optionLink').remove().appendTo(that.element); }); }, receive: function(event, ui) { ui.item.data('optionLink').attr('selected', true); // increment count that.count += 1; that._updateCount(); // workaround, because there's no way to reference // the new element, see http://dev.jqueryui.com/ticket/4303 that.selectedList.children('.ui-draggable').each(function() { $(this).removeClass('ui-draggable'); $(this).data('optionLink', ui.item.data('optionLink')); $(this).data('idx', ui.item.data('idx')); that._applyItemState($(this), true); }); // workaround according to http://dev.jqueryui.com/ticket/4088 setTimeout(function() { ui.item.remove(); }, 1); } }); } // set up livesearch if (this.options.searchable) { this._registerSearchEvents(this.availableContainer.find('input.search')); } else { $('.search').hide(); } // batch actions this.container.find(".remove-all").click(function() { that._populateLists(that.element.find('option').removeAttr('selected')); return false; }); this.container.find(".add-all").click(function() { var options = that.element.find('option').not(":selected"); if (that.availableList.children('li:hidden').length > 1) { that.availableList.children('li').each(function(i) { if ($(this).is(":visible")) $(options[i-1]).attr('selected', 'selected'); }); } else { options.attr('selected', 'selected'); } that._populateLists(that.element.find('option')); return false; }); }, destroy: function() { this.element.show(); this.container.remove(); $.Widget.prototype.destroy.apply(this, arguments); }, _populateLists: function(options) { this.selectedList.children('.ui-element').remove(); this.availableList.children('.ui-element').remove(); this.count = 0; var that = this; var items = $(options.map(function(i) { var item = that._getOptionNode(this).appendTo(this.selected ? that.selectedList : that.availableList).show(); if (this.selected) that.count += 1; that._applyItemState(item, this.selected); item.data('idx', i); return item[0]; })); // update count this._updateCount(); that._filter.apply(this.availableContainer.find('input.search'), [that.availableList]); }, _updateCount: function() { this.element.trigger('change'); this.selectedContainer.find('span.count').text(this.count+" "+$.ui.multiselect.locale.itemsCount); }, _getOptionNode: function(option) { option = $(option); var node = $('<li class="ui-state-default ui-element" title="'+option.text()+'" data-selected-value="' + option.val() + '"><span class="ui-icon"/>'+option.text()+'<a href="#" class="action"><span class="ui-corner-all ui-icon"/></a></li>').hide(); node.data('optionLink', option); return node; }, // clones an item with associated data // didn't find a smarter away around this _cloneWithData: function(clonee) { var clone = clonee.clone(false,false); clone.data('optionLink', clonee.data('optionLink')); clone.data('idx', clonee.data('idx')); return clone; }, _setSelected: function(item, selected) { item.data('optionLink').attr('selected', selected); if (selected) { var selectedItem = this._cloneWithData(item); item[this.options.hide](this.options.animated, function() { $(this).remove(); }); selectedItem.appendTo(this.selectedList).hide()[this.options.show](this.options.animated); this._applyItemState(selectedItem, true); return selectedItem; } else { // look for successor based on initial option index var items = this.availableList.find('li'), comparator = this.options.nodeComparator; var succ = null, i = item.data('idx'), direction = comparator(item, $(items[i])); // TODO: test needed for dynamic list populating if ( direction ) { while (i>=0 && i<items.length) { direction > 0 ? i++ : i--; if ( direction != comparator(item, $(items[i])) ) { // going up, go back one item down, otherwise leave as is succ = items[direction > 0 ? i : i+1]; break; } } } else { succ = items[i]; } var availableItem = this._cloneWithData(item); succ ? availableItem.insertBefore($(succ)) : availableItem.appendTo(this.availableList); item[this.options.hide](this.options.animated, function() { $(this).remove(); }); availableItem.hide()[this.options.show](this.options.animated); this._applyItemState(availableItem, false); return availableItem; } }, _applyItemState: function(item, selected) { if (selected) { if (this.options.sortable) item.children('span').addClass('ui-icon-arrowthick-2-n-s').removeClass('ui-helper-hidden').addClass('ui-icon'); else item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon'); item.find('a.action span').addClass('ui-icon-minus').removeClass('ui-icon-plus'); this._registerRemoveEvents(item.find('a.action')); } else { item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon'); item.find('a.action span').addClass('ui-icon-plus').removeClass('ui-icon-minus'); this._registerAddEvents(item.find('a.action')); } this._registerDoubleClickEvents(item); this._registerHoverEvents(item); }, // taken from John Resig's liveUpdate script _filter: function(list) { var input = $(this); var rows = list.children('li'), cache = rows.map(function(){ return $(this).text().toLowerCase(); }); var term = $.trim(input.val().toLowerCase()), scores = []; if (!term) { rows.show(); } else { rows.hide(); cache.each(function(i) { if (this.indexOf(term)>-1) { scores.push(i); } }); $.each(scores, function() { $(rows[this]).show(); }); } }, _registerDoubleClickEvents: function(elements) { if (!this.options.doubleClickable) return; elements.dblclick(function(ev) { if ($(ev.target).closest('.action').length === 0) { // This may be triggered with rapid clicks on actions as well. In that // case don't trigger an additional click. elements.find('a.action').click(); } }); }, _registerHoverEvents: function(elements) { elements.removeClass('ui-state-hover'); elements.mouseover(function() { $(this).addClass('ui-state-hover'); }); elements.mouseout(function() { $(this).removeClass('ui-state-hover'); }); }, _registerAddEvents: function(elements) { var that = this; elements.click(function() { var item = that._setSelected($(this).parent(), true); that.count += 1; that._updateCount(); return false; }); // make draggable if (this.options.sortable) { elements.each(function() { $(this).parent().draggable({ connectToSortable: that.selectedList, helper: function() { var selectedItem = that._cloneWithData($(this)).width($(this).width() - 50); selectedItem.width($(this).width()); return selectedItem; }, appendTo: that.container, containment: that.container, revert: 'invalid' }); }); } }, _registerRemoveEvents: function(elements) { var that = this; elements.click(function() { that._setSelected($(this).parent(), false); that.count -= 1; that._updateCount(); return false; }); }, _registerSearchEvents: function(input) { var that = this; input.focus(function() { $(this).addClass('ui-state-active'); }) .blur(function() { $(this).removeClass('ui-state-active'); }) .keypress(function(e) { if (e.keyCode == 13) return false; }) .keyup(function() { that._filter.apply(this, [that.availableList]); }); } }); $.extend($.ui.crossselectbox, { locale: { addAll:'Add all', removeAll:'Remove all', itemsCount:'items selected' } }); })(jQuery); $(function () { $(".crossselectbox").crossselectbox(); });

Related: See More


Questions / Comments: