简体   繁体   English

禁用Select2输入时选择当前项目

[英]Select current item when tabbing off Select2 input

This has been the subject of the following similar SO Question and several github issues including: 这是以下类似的 SO问题和几个github问题的主题,包括:

But the suggested solutions or questions have treated all blur events equally , regardless of how they were invoked. 但是建议的解决方案或问题已平等地对待所有模糊事件 ,无论如何调用它们。 With most answers leveraging Automatic selection by setting selectOnClose . 对于大多数答案,通过设置selectOnClose来利用自动选择

Ideally, clicking off the dropdown (escaping) after merely hovering over options should not change the value: 理想情况下,仅将鼠标悬停在选项上之后单击下拉列表(转义)不应更改该值:

SelectOnClose违反最小惊奇原则

How can you update the selection on tabout, but not other close events? 如何在跳出时更新选择,但不能更新其他关闭事件?

Here's an MCVE in jsFiddle and StackSnippets: 这是jsFiddle和StackSnippets中的MCVE:

 $('.select2').select2({}); 
 <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.js"></script> <div class="form-control"> <label for="foods2">Select2</label> <select id="foods2" class="select2" > <option value="1">Apple</option> <option value="2">Banana</option> <option value="3">Carrot</option> <option value="4">Donut</option> </select> </div> 

This could be trivially handled by modifying the original source code on line 323 which treats tabs and esc keys identically: 这可以通过修改第323行上的原始源代码来简单地处理,该源代码对选项卡和esc键的处理相同:

if (key === KEYS.ESC || key === KEYS.TAB || (key === KEYS.UP && evt.altKey)) {
    self.close();
    evt.preventDefault();
}

But third party libraries should ideally be modified with a pull request. 但是,理想情况下,应通过请求请求来修改第三方库。 So we have a couple problems in creating a wrapper for this. 因此,在为此创建包装器时遇到了两个问题。 Principally, detecting a tab keypress event is hard . 原则上,检测选项卡 keypress事件很难 If we catch it too late, another action might have superseded it or it might be fired on another field altogether. 如果我们为时已晚,可能会取代其他动作,或者将其完全发射到另一个领域。

The landscape of capturing tab events and persisting information seems to fall into two buckets: 捕获选项卡事件和保留信息的情况似乎分为两个部分:

  1. Monitor before tab press and also after closed 按下制表符之前和关闭之后进行监视
  2. Intercept tab press and modify synchronously 拦截制表符按下并同步修改

In either case, we must know that a tab key was the offending item that caused the menu to close. 无论哪种情况,我们都必须知道Tab键是导致菜单关闭的有问题的项目。

If we listen for keypress events with tab, on an open input, they'll either occur from .select2-selection if there's no search or select2-search__field if search is enabled. 如果我们使用tab监听按键事件,则在打开的输入中,如果没有搜索,则它们将从.select2-selection发生;如果启用了搜索,则它们将从select2-search__field

$("body").on('keydown', e => { if (e.keyCode === 9) console.log(e.target) });

选择2选项卡事件

However , if we setup as a delegated handler, as we have above, by the time the event bubbles up all the way up to "body", the menu has already closed, thus clearing the currently highlighted item and even our indicator of whether or not we started as open. 但是 ,如果我们像上面那样将其设置为委托处理程序,则当事件一直持续到“ body”时,菜单已经关闭,从而清除了当前突出显示的项目,甚至清除了我们是否指示我们不是从开放开始的。

We can intercept before the menu closes by registering for the select2:closing event like this: 我们可以通过注册select2:closing事件来在菜单关闭之前进行拦截如下所示:

$("body").on('select2:closing', e => { console.log(e,e.target) });

However , select 2 doesn't persist the original event information and instead makes their own new jQueryEvent , so we don't yet know if we're closing due to a tab event (the body.keypress event fires afterward) 但是 ,select 2不会保留原始事件信息,而是创建自己的新jQueryEvent ,因此我们尚不知道是否由于tab事件而关闭( body.keypress事件随后触发)


Solution

We'll monitor the select2:closing event and capture what we need to know. 我们将监视select2:closing事件并捕获我们需要知道的内容。 Next we need to attach a handler that listens for the subsequent firing of the initial click or a key stroke as the event pipeline is finished. 接下来,我们需要附加一个处理程序,以侦听事件管道完成后对初始单击或击键的后续触发。 We need to fire this once and only once for every close option. 对于每个关闭选项,我们只需要触发一次 To do so we can use this extension $.fn.once . 为此,我们可以使用扩展名$.fn.once If it was raised by a tab, it'll update whatever value detected during closing. 如果通过标签将其抬起,它将更新在关闭期间检测到的任何值。 If not, that value and handler will disappear. 如果没有,该值和处理程序将消失。

All told, it should look like this: 总而言之,它应该像这样:

// monitor every time we're about to close a menu
$("body").on('select2:closing', function (e) {
  // save in case we want it
  var $sel2 = $(e.target).data("select2");
  var $sel = $sel2.$element;
  var $selDropdown = $sel2.$results.find(".select2-results__option--highlighted")
  var newValue =  $selDropdown.data("data").element.value;

  // must be closed by a mouse or keyboard - listen when that event is finished
  // this must fire once and only once for every possible menu close 
  // otherwise the handler will be sitting around with unintended side affects
  $("html").once('keyup mouseup', function (e) {

    // if close was due to a tab, use the highlighted value
    var KEYS = { UP: 38, DOWN: 40, TAB: 9 }
    if (e.keyCode === KEYS.TAB) {
      if (newValue != undefined) {
        $sel.val(newValue);
        $sel.trigger('change');
      }
    }

  });

});

$.fn.once = function (events, callback) {
    return this.each(function () {
        $(this).on(events, myCallback);
        function myCallback(e) {
            $(this).off(events, myCallback);
            callback.call(this, e);
        }
    });
};

Working demo in jsFiddle and StackSnippets: jsFiddle和StackSnippets中的工作演示:

 $('.select2').select2({}); // monitor every time we're about to close a menu $("body").on('select2:closing', function (e) { // save in case we want it var $sel2 = $(e.target).data("select2"); var $sel = $sel2.$element; var $selDropdown = $sel2.$results.find(".select2-results__option--highlighted") var newValue = $selDropdown.data("data").element.value; // must be closed by a mouse or keyboard - setup listener to see when that event is completely done // this must fire once and only once for every possible menu close // otherwise the handler will be sitting around with unintended side affects $("html").once('keyup mouseup', function (e) { // if close was due to a tab, use the highlighted value var KEYS = { UP: 38, DOWN: 40, TAB: 9 } if (e.keyCode === KEYS.TAB) { if (newValue != undefined) { $sel.val(newValue); $sel.trigger('change'); } } }); }); $.fn.once = function (events, callback) { return this.each(function () { $(this).on(events, myCallback); function myCallback(e) { $(this).off(events, myCallback); callback.call(this, e); } }); }; 
 .form-control { padding:10px; display:inline-block; } select { width: 100px; border: 1px solid #aaa; border-radius: 4px; height: 28px; } 
 <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.js"></script> <div class="form-control"> <label for="foods2">Select2</label> <select id="foods2" class="select2" > <option value="1">Apple</option> <option value="2">Banana</option> <option value="3">Carrot</option> <option value="4">Donut</option> </select> </div> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM