Chosen: Keep Multiple Selection Order

2019-01-23 12:16发布

问题:

I am using Chosen's Multiple selection. I want to be able to track user selected order. For example, if a user select option2 first then option1, I want to get the result in the order of option2 and option1. But chosen sorts user selected options. Is there a way I can tell chosen to not sorting the result?

回答1:

I wrote a simple plugin to use along with Chosen. It allows you to retrieve the selection order of a Chosen multiple select element, and also to set it from an array of ordered values.

It is available on Github : https://github.com/tristanjahier/chosen-order

It is compatible with Chosen 1.0+ and with both jQuery and Prototype versions. There is also a plugin for each framework, which let you do things like this:

Retrieve the selection order

var selection = $('#my-list').getSelectionOrder();

Set the selected values in order

var order = ['nioup', 'plop', 'fianle']; // Ordered options values
$('#my-list').setSelectionOrder(order);

Check it out.



回答2:

I suggest you use Select2 if it is not too late. It looks the same as Chosen but has a way better API and docs.

With select2, it would be something like [untested code]

 var results = [];
 $("#e11").on("change", function(e) { 
    results.push(e.val)
 })


回答3:

There is a https://github.com/mrhenry/jquery-chosen-sortable/ plugin now available, which does the job for chosen v1.

For newer versions of chosen, you need to replace chzn- with chosen- in the github JS file.

To use, it I use the following code:

jQuery('#myselect').addClass('chosen-sortable').chosenSortable();

It works very well, but you will need some more code, when editing the field again to re-order the elements after chosen has loaded.

I use the following for this. Given a list of sortedElements and $select = jQuery('#myselect'):

var $container = $select.siblings('.chosen-container')
var $list = $container.find('.chosen-choices');

// Reverse the list, as we want to prepend our elements.
var sortedElements = sortedElements.reverse();

for (var i = 0; i < sortedElements.length; i++) {
  var value = sortedElements[i];

  // Find the index of the element.
  var index = $select.find('option[value="' + value + '"]').index();

  // Sort the item first.
  $list
          .find('a.search-choice-close[data-option-array-index="' + index + '"]')
          .parent()
          .remove()
          .prependTo($list);
}

This works very well here.



回答4:

I injected a custom .change() event handler to make it work:

$('.chosen-select').chosen({});

to:

$('.chosen-select').chosen({}).change((event, info) => {
  if (info.selected) {
    var allSelected = this.querySelectorAll('option[selected]');
    var lastSelected = allSelected[allSelected.length - 1];
    var selected = this.querySelector(`option[value="${info.selected}"]`);
    selected.setAttribute('selected', '');
    lastSelected.insertAdjacentElement('afterEnd', selected);
  } else { // info.deselected
    var removed = this.querySelector(`option[value="${info.deselected}"]`);
    removed.setAttribute('selected', false); // this step is required for Edge
    removed.removeAttribute('selected');
  }
  $(this).trigger("chosen:updated");
});

Note this moves the DOM in case of selection and no movement in case of deselection. The setAttribute/removeAttribute is to bring about visible change in <option> DOM.

Hope this help someone still looking for a way to do it without doing anything extra on submit.

PS: compact jQuery version of this code can be written if needed. :)



回答5:

Hope this is useful for folks who use older version of select2 (less than v4). select2-selecting and select2-removing options are helpful in this case. Using an array and appending the values once we select, we can maintain the order of selection and while de-selecting a particular option, we can find the index and remove it from the array using splice().

var selected = [];    
$('#multiSelect').on("select2-selecting", function(evt) {
            var selectedOption = evt.val;
            selected.push(selectedOption);

        }).on("select2-removing", function(evt) {
              index = selected.indexOf(evt.val);
              selected.splice(index,1);       
        });   

For version 4 and above, select2:select and select2:unselect can be used. :)



回答6:

This will arrange in order of your selection

$("select").on('select2:select', function (e) {
    var elm = e.params.data.element;
    $elm = jQuery(elm);
    $t = jQuery(this);
    $t.append($elm);
    $t.trigger('change.select2');
});