39

我有一个包含多个文本输入和一些 select2 元素的表单。使用键盘在字段之间切换可以正常工作 - Select2 元素的行为类似于表单元素,并且在切换时接收焦点。我想知道当 Select2 元素获得焦点时是否可以打开下拉列表。

这是我到目前为止所尝试的:

$("#myid").select2().on('select2-focus', function(){
     $(this).select2('open');
});

但是使用此代码会使下拉菜单在进行选择后再次打开。

4

17 回答 17

80

v4.0+的工作代码*(包括4.0.7)

以下代码将在初始焦点上打开菜单,但在菜单关闭后选择重新聚焦时不会陷入无限循环。

// on first focus (bubbles up to document), open the menu
$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
  $(this).closest(".select2-container").siblings('select:enabled').select2('open');
});

// steal focus during close - only capture once and stop propogation
$('select.select2').on('select2:closing', function (e) {
  $(e.target).data("select2").$selection.one('focus focusin', function (e) {
    e.stopPropagation();
  });
});

解释

防止无限焦点循环

注意:该focus事件被触发两次

  1. 进入该字段时一次
  2. 再次使用打开的下拉菜单进行选项卡以恢复焦点

焦点菜单状态

我们可以通过寻找焦点事件类型之间的差异来防止无限循环。由于我们只想在控件的初始焦点上打开菜单,因此我们必须以某种方式区分以下引发的事件:

活动时间线

这样做很难以跨浏览器友好的方式进行,因为浏览器会随不同的事件发送不同的信息,而且 Select2 对其内部事件触发进行了许多细微的更改,这会中断先前的流程。

一种似乎可行的方法是closing在菜单事件期间附加一个事件处理程序,并使用它来捕获即将发生的focus事件并防止它冒泡 DOM。然后,使用委托侦听器,我们将调用实际焦点 -> 只有当focus事件一直冒泡到document

防止打开禁用的选择

正如这个 github 问题#4025 - Dropdown does not open on tab focus中所述,我们应该检查以确保我们只调用'open'这样的:enabled选择元素:

$(this).siblings('select:enabled').select2('open');

Select2 DOM 遍历

我们要稍微遍历一下DOM,所以这里是Select2生成的HTML结构图

选择2 DOM

GitHub上的源代码

以下是一些相关的代码部分:

.on('mousedown'…………………… .trigger('toggle')
.on('toggle' _ .toggleDropdown()
.toggleDropdown_ .open()
.on('focus'_ .trigger('focus'
.on('close'_$selection.focus()

以前打开 select2 会触发两次,但它已在问题 #3503中修复,这应该可以防止出现一些卡顿

PR #5357似乎破坏了之前在 4.05 中工作的焦点代码

jsFiddle和 Stack Snippets 中的工作演示:

$('.select2').select2({});

// on first focus (bubbles up to document), open the menu
$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
  $(this).closest(".select2-container").siblings('select:enabled').select2('open');
});

// steal focus during close - only capture once and stop propogation
$('select.select2').on('select2:closing', function (e) {
  $(e.target).data("select2").$selection.one('focus focusin', function (e) {
    e.stopPropagation();
  });
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/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.7/js/select2.js"></script>

<select class="select2" style="width:200px" >
  <option value="1">Apple</option>
  <option value="2">Banana</option>
  <option value="3">Carrot</option>
  <option value="4">Donut</option>
</select>

在 Chrome、FF、Edge、IE11 上测试

于 2018-03-13T16:38:49.837 回答
29

对于版本 3.5.4 (2015 年 8 月 30 日及更早)

当前答案仅适用于 3.5.4 及之前的版本,其中 select2 触发了模糊和焦点事件( select2-focus& select2-blur)。它附加一个一次性使用处理程序$.one来捕获初始焦点,然后在模糊期间重新附加它以供后续使用。

$('.select2').select2({})
  .one('select2-focus', OpenSelect2)
  .on("select2-blur", function (e) {
    $(this).one('select2-focus', OpenSelect2)
  })

function OpenSelect2() {
  var $select2 = $(this).data('select2');
  setTimeout(function() {
    if (!$select2.opened()) { $select2.open(); }
  }, 0);  
}

我尝试了@irvin-dominin-aka-edward 的两个答案,但也遇到了这两个问题(必须单击下拉菜单两次,并且 Firefox 抛出“未定义事件”)。

我确实找到了一个似乎可以解决这两个问题并且还没有遇到其他问题的解决方案。这是基于@irvin-dominin-aka-edward 的答案,通过修改 select2Focus 函数而不是立即执行其余代码,而是将其包装在 setTimeout 中。

jsFiddle和 Stack Snippets中的演示

$('.select2').select2({})
  .one('select2-focus', OpenSelect2)
  .on("select2-blur", function (e) {
    $(this).one('select2-focus', OpenSelect2)
  })

function OpenSelect2() {
  var $select2 = $(this).data('select2');
  setTimeout(function() {
    if (!$select2.opened()) { $select2.open(); }
  }, 0);  
}
body {
  margin: 2em;
}

.form-control {
  width: 200px;  
  margin-bottom: 1em;
  padding: 5px;
  display: flex;
  flex-direction: column;
}

select {
  border: 1px solid #aaa;
  border-radius: 4px;
  height: 28px;
}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.4/select2.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.4/select2.js"></script>

  
  <div class="form-control">
    <label for="foods1" >Normal</label>
    <select id="foods1" >
      <option value=""></option>
      <option value="1">Apple</option>
      <option value="2">Banana</option>
      <option value="3">Carrot</option>
      <option value="4">Donut</option>
    </select>
</div>

<div class="form-control">
  <label for="foods2" >Select2</label>
  <select id="foods2" class="select2" >
    <option value=""></option>
    <option value="1">Apple</option>
    <option value="2">Banana</option>
      <option value="3">Carrot</option>
      <option value="4">Donut</option>
    </select>
  </div>

于 2014-03-05T21:51:52.797 回答
25

可以在页面上的所有 select2 实例上运行的简单方法。

$(document).on('focus', '.select2', function() {
    $(this).siblings('select').select2('open');
});

更新:上面的代码在IE11/Select2 4.0.3上似乎不能正常工作

PS:还添加了过滤器以仅single选择选择字段。Select with multipleattribute 不需要它,如果应用它可能会中断。

var select2_open;
// open select2 dropdown on focus
$(document).on('focus', '.select2-selection--single', function(e) {
    select2_open = $(this).parent().parent().siblings('select');
    select2_open.select2('open');
});

// fix for ie11
if (/rv:11.0/i.test(navigator.userAgent)) {
    $(document).on('blur', '.select2-search__field', function (e) {
        select2_open.select2('close');
    });
}
于 2015-07-31T19:17:23.107 回答
17

可能在做出选择之后select2-focus触发了一个事件。

我发现的唯一方法是select2-focusand select2-blurevent 和 jQueryone事件处理程序的组合。

所以第一次元素获得焦点时,select2 打开一次(因为一次),当元素模糊时,再次附加一个事件处理程序,依此类推。

代码:

$('#test').select2({
    data: [{
        id: 0,
        text: "enhancement"
    }, {
        id: 1,
        text: "bug"
    }, {
        id: 2,
        text: "duplicate"
    }, {
        id: 3,
        text: "invalid"
    }, {
        id: 4,
        text: "wontfix"
    }],
    width: "300px"
}).one('select2-focus', select2Focus).on("select2-blur", function () {
    $(this).one('select2-focus', select2Focus)
})

function select2Focus() {
    $(this).select2('open');
}

演示:http: //jsfiddle.net/IrvinDominin/fnjNb/

更新

要让鼠标单击起作用,您必须检查触发处理程序的事件,它必须open仅在事件发生时触发方法focus

代码:

function select2Focus() {
    if (/^focus/.test(event.type)) {
        $(this).select2('open');
    }
}

演示:http: //jsfiddle.net/IrvinDominin/fnjNb/4/

SELECT2 V 4.0 的更新

select2 v 4.0 已更改其 API 并删除了自定义事件(请参阅https://github.com/select2/select2/issues/1908)。所以有必要改变检测焦点的方式。

代码:

$('.js-select').select2({
    placeholder: "Select",
    width: "100%"
})

$('.js-select').next('.select2').find('.select2-selection').one('focus', select2Focus).on('blur', function () {
    $(this).one('focus', select2Focus)
})

function select2Focus() {
    $(this).closest('.select2').prev('select').select2('open');
}

演示:http: //jsfiddle.net/IrvinDominin/xfmgte70/

于 2014-01-08T08:03:56.883 回答
10

有点晚了......但使用 select2 4.0.0 分享我的代码

$("#my_id").select2();
$("#my_id").next(".select2").find(".select2-selection").focus(function() {
    $("#my_id").select2("open");
});
于 2015-06-22T22:29:52.113 回答
7

这是 Select2 版本 4.x 的替代解决方案。您可以使用侦听器来捕获焦点事件,然后打开选择。

$('#test').select2({
    // Initialisation here
}).data('select2').listeners['*'].push(function(name, target) { 
    if(name == 'focus') {
        $(this.$element).select2("open");
    }
});

在这里找到基于@tonywchen 创建的示例的工作示例

于 2016-06-20T15:26:49.910 回答
5

问题是,内部焦点事件没有转换为 jQuery 事件,所以我修改了插件并将焦点事件添加到 Select2 4.0.3 的第 2063 行的 EventRelay 中:

EventRelay.prototype.bind = function (decorated, container, $container) {
    var self = this;
    var relayEvents = [
      'open', 'opening',
      'close', 'closing',
      'select', 'selecting',
      'unselect', 'unselecting',
      'focus'
    ]};

然后在焦点发生时打开select2就足够了:

$('#select2').on('select2:focus', function(evt){
    $(this).select2('open');
});

在 Chrome 54、IE 11、FF 49、Opera 40 上运行良好

于 2016-10-14T21:57:47.630 回答
5

KyleMit 的回答对我有用(谢谢!),但我注意到使用允许搜索的 select2 元素,尝试跳到下一个元素是行不通的(跳格顺序实际上丢失了),所以我添加了代码来重新设置焦点当下拉菜单关闭时到主 select2 元素:

$(document).on('focus', '.select2', function (e) {
    if (e.originalEvent) {
        var s2element = $(this).siblings('select');
        s2element.select2('open');

        // Set focus back to select2 element on closing.
        s2element.on('select2:closing', function (e) {
            s2element.select2('focus');
        });
    }
});
于 2018-05-25T18:53:57.307 回答
3

对于使用 Select2.full.js 版本 4.0.3 的我来说,上述解决方案都没有按应有的方式工作。因此,我编写了上述解决方案的组合。首先,我修改了 Select2.full.js 以将内部焦点和模糊事件转移到 jquery 事件,正如“Thomas Molnar”在他的回答中所做的那样。

EventRelay.prototype.bind = function (decorated, container, $container) {
    var self = this;
    var relayEvents = [
      'open', 'opening',
      'close', 'closing',
      'select', 'selecting',
      'unselect', 'unselecting',
      'focus', 'blur'
    ];

然后我添加了以下代码来处理焦点和模糊以及聚焦下一个元素

$("#myId").select2(   ...   ).one("select2:focus", select2Focus).on("select2:blur", function ()
{
    var select2 = $(this).data('select2');
    if (select2.isOpen() == false)
    {
        $(this).one("select2:focus", select2Focus);
    }
}).on("select2:close", function ()
{
    setTimeout(function ()
    {
        // Find the next element and set focus on it.
        $(":focus").closest("tr").next("tr").find("select:visible,input:visible").focus();            
    }, 0);
});
function select2Focus()
{
    var select2 = $(this).data('select2');
    setTimeout(function() {
        if (!select2.isOpen()) {
            select2.open();
        }
    }, 0);  
}
于 2017-01-08T09:39:00.883 回答
3

我尝试了其中的一些,最后想出了以下适用于我的 Select2 4.0.1。元素是<select>元素。

$.data(element).select2.on("focus", function (e) {
    $(element).select2("open");
});
于 2016-01-27T15:29:57.803 回答
2

重要的是始终保持多选打开。最简单的方法是在代码中的“条件”上触发打开事件:

<select data-placeholder="Choose a Country..." multiple class="select2-select" id="myList">
    <option value="United States">United States</option>
    <option value="United Kingdom">United Kingdom</option>
    <option value="Afghanistan">Afghanistan</option>
    <option value="Aland Islands">Aland Islands</option>
    <option value="Albania">Albania</option>
    <option value="Algeria">Algeria</option>
</select>

javascript:

$(".select2-select").select2({closeOnSelect:false});
$("#myList").select2("open");

小提琴:http: //jsfiddle.net/xpvt214o/153442/

于 2018-04-18T16:26:06.987 回答
2

我遇到了两个方面的问题:
1. 在具有多个 select2 元素的表单中,下拉菜单不会在选项卡上打开,您需要按空格键才能打开它
2. 做出选择后, tabindex 将不会被兑现,您必须手动单击下一个输入字段

虽然通常的建议有效,但我提出了自己的版本,因为库脚本正在将普通选择转换为 select2,因此我无法控制此初始化。

这是对我有用的代码。

选项卡打开

$(document).on("focus", ".select2", function() {
    $(this).siblings("select").select2("open");
});

移动到下一个选择

var inputs = $("input,select"); // You can use other elements such as textarea, button etc. 
                                //depending on input field types you have used
$("select").on("select2:close",function(){
    var pos = $(inputs).index(this) + 1;
    var next = $(inputs).eq(pos);
    setTimeout( function() {
        next.focus();
        if (next.siblings(".select2").length) { //If it's a select
            next.select2("open");
        }
    }, 500); //The delay is required to allow default events to occur
});

希望这可以帮助。

于 2017-06-20T05:01:49.337 回答
1

这对我使用 Select2 v4.0.3 有效

//Initialize Select2
 jQuery('.js-select').select2();

// Make Select2 respect tab focus
function select2Focus(){
    jQuery(window).keyup(function (e) {
        var code = (e.keyCode ? e.keyCode : e.which);
        if (code == 9 && jQuery('.select2-search__field:focus').length) {
            jQuery('.js-select').select2('open');
        }
    });
}

select2Focus();

Irvin Dominin 演示的分叉:http: //jsfiddle.net/163cwdrw/

于 2017-03-21T05:39:06.447 回答
0

我用 select2 版本 3.4.8 尝试了这些解决方案,发现当你这样做时blur,select2 先触发select2-close然后select2-focus再触发select2-blur,所以最后我们最终会永远重新打开 select2。

然后,我的解决方案是这个:

$('#elemId').on('select2-focus', function(){
    var select2 = $(this).data('select2');
    if( $(this).data('select2-closed') ){
        $(this).data('select2-closed', false)
        return
    }
    if (!select2.opened()) {
        select2.open()
    }
}).on('select2-close', function(){
    $(this).data('select2-closed', true)
})
于 2014-05-06T14:32:22.807 回答
0

不知何故,select2Focus 在此处无法使用空选择,无法解决问题,因此我在触发焦点事件自动打开获取后添加了手动控制。

这是咖啡脚本:

$("#myid").select2()
  .on 'select2-blur', ->
    $(this).data('select2-auto-open', 'true')
  .on 'select2-focus', ->
    $(this).data('select2').open() if $(this).data('select2-auto-open') != 'false'
  .on 'select2-selecting', ->
    $(this).data('select2-auto-open', 'false')
于 2014-06-06T09:22:03.300 回答
0

你可以使用这个:

 $(document).on('select2:open', () => {
    document.querySelector('.select2-search__field').focus();
  });
于 2021-05-03T04:44:41.250 回答
0

我尝试了一个非常丑陋的解决方案,但它解决了我的问题。

    var tabPressed = false;

    $(document).keydown(function (e) {
        // Listening tab button.
        if (e.which == 9) {
            tabPressed = true;
        }
    });

    $(document).on('focus', '.select2', function() {
        if (tabPressed) {
            tabPressed = false;
            $(this).siblings('select').select2('open');
        }
    });
于 2016-05-18T12:03:16.157 回答