Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Select2 4.0.0 AJAX - Choose highlighted option with Tab

I'm fetching user IDs and names via AJAX and using Select2 to search through them, but my users have requested the ability to select from the typeahead dropdown by pressing Tab, effectively treating it like pressing Enter. Here is my select2 declaration:

$("#user-select").select2({
    ajax: {
        url: "/api/User",
        method: "get",
        data: function (params) {
            return {
                search: params.term
            };
        },
        beforeSend: function () {
            $(".loading-results").text("Loading...");
        },
        processResults: function (data) {
            return {
                results: data
            };
        },
        cache: true
    },
    allowClear: true,
    placeholder: "Enter a User ID or Name",
    templateResult: function (data) {
        return "(" + data.id + ") " + data.name;
    },
    templateSelection: function (data) {
        return "(" + data.id + ") " + data.name;
    }

".select2-search__field" seems to be the focused element whenever the dropdown's visible, and the highlighted element gets the class "select2-results__option--highlighted".

I've tried a few solutions, but nothing seems to have worked, especially because this element appears and disappears anytime the dropdown opens. Unfortunately I lost the code from my attempts, but they consisted mainly of doing preventDefault when Tab is hit on the focused input and then triggering a click event on the highlighted element or triggering the enter key on the input.

I also tried adjusting the selectOnClose option, but that seems buggy in general and caused an endless loop when I had it running normally, much less trying to override it based on what key is being pressed.

[Edit]
The selected solution works, but doesn't account for the templateResult specified, instead showing "() undefined". So, I tweaked it to add the highlighted answer as the selected Option for the overlying Select, and then call the change event right on that Select.

...(Same as initial select2)

}).on('select2:close', function (evt) {
    var context = $(evt.target);

    $(document).on('keydown.select2', function (e) {
        if (e.which === 9) { // tab
            var highlighted = context.data('select2').$dropdown.find('.select2-results__option--highlighted');

            if (highlighted) {
                var data = highlighted.data('data');

                var id = data.id;
                var display = data.name;

                $("#user-select").html("<option value='" + id + "' selected='selected'>" + display + "</option>");
                $("#user-select").change();
            }
            else {
                context.val("").change();
            }
        }
    });
like image 366
MikeOShay Avatar asked Nov 03 '15 02:11

MikeOShay


People also ask

How does Select2 handle options in dropdown menus?

Each of these is rendered as an option in the dropdown menu. Select2 preserves this behavior when initialized on a <select> element that contains <option> elements, converting them into its internal JSON representation:

How does Select2 handle disabled options?

Disabling options Select2 will correctly handle disabled options, both with data coming from a standard select (when the disabled attribute is set) and from remote sources, where the object has disabled: true set.

How do I change the select element after another select element?

For example: You might want to change the SELECT element after another SELECT element has been changed. In that case, you would modify the code above and wrap it inside the onchange event handler. Inside the success handler of our AJAX request, we used the $.each function to loop through each element in the JSON array that our PHP script returned.

How are <optgroup> and <option> rendered in Select2?

Each of these is rendered as an option in the dropdown menu. Select2 preserves this behavior when initialized on a <select> element that contains <option> elements, converting them into its internal JSON representation: <optgroup> elements will be converted into data objects using the following rules:


3 Answers

The selectOnClose feature seems to be stable in 4.0.3, and a much simpler solution:

$("#user-select").select2({
  ...
  selectOnClose: true
});

It's possible that the use of templates interferes with this feature, I'm not using one so I haven't tested that.

like image 161
Dylan Smith Avatar answered Oct 13 '22 14:10

Dylan Smith


I am using the select2 version 4.0.6-rc.1 with vue, this is what I did to keep the binding safe:

selectElement
.select2({ options ... })
.on("select2:close", function(evt) {
      var context = $(evt.target);

      $(document).on("keydown.select2", function(e) {
          if (e.which === 9) {
              var highlighted = context
                  .data("select2")
                  .$dropdown.find(".select2-results__option--highlighted");

              if (highlighted) {
                  $.fn.select2.amd.require(["select2/utils"], function(Utils) {
                      var data = Utils.__cache[highlighted.data().select2Id].data;
                      var $select2 = context.data('select2');
                      $select2.trigger("select", {data: data});
                  });
              }
          }
      });

      setTimeout(function() {
        $(document).off("keydown.select2");
      }, 1);
  });

For me the key was the Utils helper which is part of the library, retrieve the list from the cache of the current element and then forcing the select with the new value.

Good luck! :)

like image 40
levieraf Avatar answered Oct 13 '22 15:10

levieraf


After playing around with all these solutions, this one seems to catch the most cases and work the best for me. Note I am using select2 4.0.3 but did not like the selectOnClose, if you have multiple select2 boxes with multiple it can wreak havoc!

var fixSelect2MissingTab = function (event) {
    var $selected_id_field = $(event.target);

    var selectHighlighted = function (e) {
        if (e.which === 9) {
            var highlighted = $selected_id_field.data('select2').$dropdown.find('.select2-results__option--highlighted');

            if (highlighted) {
                var data = highlighted.data('data');
                if (data) {
                    var vals = $selected_id_field.val();
                    if (vals === null) {
                        vals = [];
                    }
                    if (vals.constructor === Array) {
                        vals.push(data.id);
                    } else {
                        vals = data.id;
                    }
                    $selected_id_field.val(vals).trigger("change");
                }
            }
        }
    };

    $('.select2-search__field').on('keydown', selectHighlighted);       
}

$(document).on('select2:open', 'select', function (e) { fixSelect2MissingTab(e) });
$(document).on('select2:close', 'select', function (e) {
    //unbind to prevent multiple
    setTimeout(function () {
        $('.select2-search__field').off('keydown');
    }, 10);
});

The nice thing about this solution is it's generic and can be applied in framework code for that will work even for dynamically added select2 boxes.

like image 28
adameska Avatar answered Oct 13 '22 14:10

adameska