Selectable optgroup using select2

后端 未结 8 575
醉话见心
醉话见心 2020-12-02 21:20

I have used select2 to select multiple options from a drop down, but is it possible for select2 to select the full optgroup?? What I want is when user select the option grou

相关标签:
8条回答
  • 2020-12-02 21:52

    In Select2 v4, I found that John S's answer won't work (see here). I am loading my data as an array using AJAX and created a workaround:

    $(document).on("click", ".select2-results__group", function(){
        var input = $(this);
        var location = input.html();
        // Find the items with this location
        var options = $('#select2 option');
        $.each(options, function(key, value){
            var name = $(value).html();
            // The option contains the location, so mark it as selected
            if(name.indexOf(location) >= 0){
                $(value).prop("selected","selected");
            }
        });
        $("#select2").trigger("change");
    });
    

    I am grouping my items by location, and each option contains the location name somewhere in it's html. Any time an optgroup header is clicked, I get it's location (the name displayed in the dropdown). Then I look through all options in the #select2 table and find which ones contain that location in its html.

    I know this is a hacky workaround, but hopefully it helps/points in the right direction.

    0 讨论(0)
  • 2020-12-02 21:57

    John S, provided example works really well (for V3) for most of the cases. However it has one bug:

    Assuming the selection list is long enough to have scroll. When you select any item in any group that is only available after you scroll down - you can't select the next item after the one you selected from this group anymore. It is because ensureHighlightVisible method of select2 starts misbehaving because the selectors it is using made using assumptions that group is always 'unselectable'. So the scroll will jump every time you try to select the item.

    So unfortunately although this solution looks really good - I dropped it and re-implemented it without using group id's:

    $selectEl..on("select2-open", function(event) {
              $(event.target).data("select2").dropdown.on("click", "li.select2-result-unselectable", selectGroup);
              $(event.target).data("select2").dropdown.on("mousemove-filtered", "li.select2-result-unselectable", highlight);
            }).on("select2-close", function(event) {
              $(event.target).data("select2").dropdown.off("click", "li.select2-result-unselectable", selectGroup);
              $(event.target).data("select2").dropdown.off("mousemove-filtered", "li.select2-result-unselectable", highlight);
            });
    

    and

      // selection of the group.
      function selectGroup(e) {
        var $li = $(this);
        e.preventDefault();
        $select.select2('data', $select.select2('data').concat($li.data("select2Data").children));
        $select.select2('close');
        _this.$field.trigger('change');
      }
    
      // highlight of the group.
      function highlight(e) {
        if ($(e.target).hasClass("select2-result-unselectable") || $(e.target.parentNode).hasClass('select2-result-unselectable')) {
          e.preventDefault();
          e.stopPropagation();
          $select.data("select2").dropdown.find(".select2-highlighted").removeClass("select2-highlighted");
          $(this).addClass("select2-highlighted");
        }
      }
    
    0 讨论(0)
  • 2020-12-02 22:01

    This is possible if you back the Select2 with a hidden input element -- instead of a select element.

    To make a group option selectable, you must give it an "id", but it appears it can be an empty string. You can then use the "select2-selecting" event to prevent the group option from getting selected, and instead have its child options get selected.

    Additionally, a query function can be provided to prevent a group option from appearing in the list after all its child options have been selected.

    If you have options defined like this:

    var FRUIT_GROUPS = [
        {
            id: '',
            text: 'Citrus',
            children: [
                { id: 'c1', text: 'Grapefruit' },
                { id: 'c2', text: 'Orange' },
                { id: 'c3', text: 'Lemon' },
                { id: 'c4', text: 'Lime' }
            ]
        },
        {
            id: '',
            text: 'Other',
            children: [
                { id: 'o1', text: 'Apple' },
                { id: 'o2', text: 'Mango' },
                { id: 'o3', text: 'Banana' }
            ]
        }
    ];
    

    You can instrument your Select2 like this:

    $('#fruitSelect').select2({
        multiple: true,
        placeholder: "Select fruits...",
        data: FRUIT_GROUPS,
        query: function(options) {
            var selectedIds = options.element.select2('val');
            var selectableGroups = $.map(this.data, function(group) {
                var areChildrenAllSelected = true;
                $.each(group.children, function(i, child) {
                    if (selectedIds.indexOf(child.id) < 0) {
                        areChildrenAllSelected = false;
                        return false; // Short-circuit $.each()
                    }
                });
                return !areChildrenAllSelected ? group : null;
            });
            options.callback({ results: selectableGroups });
        }
    }).on('select2-selecting', function(e) {
        var $select = $(this);
        if (e.val == '') { // Assume only groups have an empty id
            e.preventDefault();
            $select.select2('data', $select.select2('data').concat(e.choice.children));
            $select.select2('close');
        }
    });
    

    jsfiddle

    Here is a jsfiddle without the query function, where the group options still appear when all of their child options are selected.

    0 讨论(0)
  • 2020-12-02 22:03

    Well, I came across this issue and I found that every time the select2 (Select2 4.0.5) opens it adds a span element before the closing body element. In addition, inside the span element adds a ul with the id: select2-X-results, where X is the select2 id. So I found the following workaround (jsfiddle):

    var countries = [{
      "id": 1,
      "text": "Greece",
      "children": [{
        "id": "Athens",
        "text": "Athens"
      }, {
        "id": "Thessalonica",
        "text": "Thessalonica"
      }]
    }, {
      "id": 2,
      "text": "Italy",
      "children": [{
        "id": "Milan",
        "text": "Milan"
      }, {
        "id": "Rome",
        "text": "Rome"
      }]
    }];
    
    $('#selectcountry').select2({
      placeholder: "Please select cities",
      allowClear: true,
      width: '100%',
      data: countries
    });
    
    $('#selectcountry').on('select2:open', function(e) {
    
      $('#select2-selectcountry-results').on('click', function(event) {
    
        event.stopPropagation();
        var data = $(event.target).html();
        var selectedOptionGroup = data.toString().trim();
    
        var groupchildren = [];
    
        for (var i = 0; i < countries.length; i++) {
    
    
          if (selectedOptionGroup.toString() === countries[i].text.toString()) {
    
            for (var j = 0; j < countries[i].children.length; j++) {
    
              groupchildren.push(countries[i].children[j].id);
    
            }
    
          }
    
    
        }
    
    
        var options = [];
    
        options = $('#selectcountry').val();
    
        if (options === null || options === '') {
    
          options = [];
    
        }
    
        for (var i = 0; i < groupchildren.length; i++) {
    
          var count = 0;
    
          for (var j = 0; j < options.length; j++) {
    
            if (options[j].toString() === groupchildren[i].toString()) {
    
              count++;
              break;
    
            }
    
          }
    
          if (count === 0) {
            options.push(groupchildren[i].toString());
          }
        }
    
        $('#selectcountry').val(options);
        $('#selectcountry').trigger('change'); // Notify any JS components that the value changed
        $('#selectcountry').select2('close');    
    
      });
    });
    li.select2-results__option strong.select2-results__group:hover {
      background-color: #ffffd;
      cursor: pointer;
    }
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet"/>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.full.min.js"></script>
    
    
    <h1>Selectable optgroup using select2</h1>
    <select id="selectcountry" name="country[]" class="form-control" multiple style="width: 100%"></select>

    0 讨论(0)
  • 2020-12-02 22:06

    I found a plugin for Select2 v4 which adds the ability to click on the optgroup to select/unselect all of child options. It worked perfectly for me. bnjmnhndrsn/select2-optgroup-select

    Thanks Ben Henderson!

    0 讨论(0)
  • 2020-12-02 22:10

    One option for V 4.0.2 using a select element:

    <select style="width:100%;" id="source" multiple="" tabindex="-1" aria-hidden="true">                 
       <optgroup class="select2-result-selectable" label="Statuses"   >        
          <option value="1">Received</option>                          
          <option value="2">Pending Acceptance</option>                             
       </optgroup>                                 
       <optgroup class="select2-result-selectable" label="Progress" >                
          <option value="6">In Progress</option>
          <option value="7">Follow Up</option>                         
      </optgroup>                                                    
    </select>
    

    JS + JQuery:

    $(document).ready(function() {
    
       $('#source').select2();
    
    $(document).on("click", ".select2-results__group", function(){
    
        var groupName = $(this).html()
        var options = $('#source option');
    
        $.each(options, function(key, value){
    
            if($(value)[0].parentElement.label.indexOf(groupName) >= 0){
                $(value).prop("selected","selected");
            }
    
        });
    
        $("#source").trigger("change");
        $("#source").select2('close'); 
    
      });
    });
    

    Fiddle: https://jsfiddle.net/un1oL8w0/4/

    0 讨论(0)
提交回复
热议问题