【问题标题】:Select2 open dropdown on focusSelect2 在焦点上打开下拉菜单
【发布时间】:2014-01-26 04:30:44
【问题描述】:

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

这是我迄今为止尝试过的:

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

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

【问题讨论】:

    标签: javascript jquery jquery-select2 jquery-select2-4


    【解决方案1】:

    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 中所述,我们应该检查以确保我们只在 :enabled 上调用 'open' 选择这样的元素:

    $(this).siblings('select<b>:enabled</b>').select2('open');

    Select2 DOM 遍历

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

    GitHub 上的源代码

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

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

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

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

    Working Demo in jsFiddle & 堆栈片段:

    $('.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 上测试

    【讨论】:

    • 谢谢你。在 Mac 上的 Chrome、FF 和 Safari 中为我工作,在快速测试中。
    • 实际上,这给我带来了一个错误。或者,我的代码如何与之交互会导致错误。如果我从 select2 中单击到文本输入字段中,则 select2 会关闭然后重新打开。铬合金。 Select2 v4.0.7.
    • 如果我将您的小提琴调整为使用 v4.0.7,它就不再起作用了。 jsfiddle.net/nwLbqfa4
    • 但是!恢复到 Select2 v4.0.5 和 Kyle 的代码仍然有效。当心 v4.0.7,所有想要打开 select2 的人!它会打开,但永远不会关闭.... :)
    • @MattOlson,找到了适用于所有 4 个以上版本的 4.0.7 解决方案
    【解决方案2】:

    对于Version 3.5.4 (2015 年 8 月 30 日及之前)

    当前答案仅适用于版本 3.5.4 及之前的版本,其中 select2 触发了 blur and focus events (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 中,而不是立即执行其余代码。

    Demo in jsFiddle & 堆栈片段

    $('.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>

    【讨论】:

    【解决方案3】:

    适用于页面上所有 select2 实例的简单方法。

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

    更新:上述代码在IE11/Select2 4.0.3

    上似乎无法正常工作

    PS:还添加了过滤器以仅选择single 选择字段。带有multiple 属性的选择不需要它,如果应用它可能会中断。

    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');
        });
    }
    

    【讨论】:

    • 这很好,虽然最近的 IE11 更新似乎破坏了它:)
    • 只有一个对我有用。我不得不将我的 jquery 和我的 select2 cdns 分别升级到 3.6 和 4.1.0。
    【解决方案4】:

    可能在进行选择后触发select2-focus 事件。

    我发现的唯一方法是 select2-focusselect2-blur 事件和 jQuery one 事件处理程序的组合。

    所以元素第一次获得焦点时,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/

    更新

    要让鼠标单击起作用,您必须检查触发处理程序的事件,只有当事件是focus 时,它才必须触发open 方法

    代码:

    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/

    【讨论】:

    • 感谢您的帮助。此解决方案适用于键盘导航。但我发现了一个错误:鼠标点击 select2 元素时,需要点击两次才能打开下拉菜单:第一次点击将焦点放在元素上,第二次将实际打开下拉菜单。
    • 我在 chrome 中测试过:工作正常。在 mozilla 中,我收到以下错误“未定义事件”。
    • 我搜索了一些有关firefox事件的信息,这似乎是一个棘手的问题。无论如何,非常感谢您的帮助。
    • @andreivictor 很高兴为您提供帮助,但我想找到解决此问题的方法(也用于复选标记 :-)
    • 示例不起作用。尝试更新到 v4.0 仍然无法正常工作:jsfiddle.net/brunodd/fnjNb/71
    【解决方案5】:

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

    $("#my_id").select2();
    $("#my_id").next(".select2").find(".select2-selection").focus(function() {
        $("#my_id").select2("open");
    });
    

    【讨论】:

    • 此解决方案在 IE 中不起作用 =/ 它会打开,但它永远不会关闭(甚至不点击外部,标签页)。在 IE 11 中测试
    • 这在您通过 ID 选择单个元素时有效,但如果您想定位多个 select2 字段(而不是每次只打开第一个字段),则有效:$('.my-class').next('.select2').find('.select2-selection').focus(function (e) { $(this).closest('.select2').prev('select..my-class').select2('open'); }); (抱歉,cmets 中显然不能有换行符。)
    • 谢谢老哥,新旧版本的信息都乱了。对我有用,只需 .select2("open") 就可以了。
    【解决方案6】:

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

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

    根据@tonywchen 创建的示例查找工作示例here

    【讨论】:

      【解决方案7】:

      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');
              });
          }
      });

      【讨论】:

      • 嘿@Douglas,我在尝试重现您在此处解决的问题时遇到了麻烦,但 4 人赞成表示其他人可能会遇到它。您何时会看到标签顺序丢失?
      【解决方案8】:

      问题是,内部的焦点事件并没有转化为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 上运行良好

      【讨论】:

        【解决方案9】:

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

        $.data(element).select2.on("focus", function (e) {
            $(element).select2("open");
        });
        

        【讨论】:

          【解决方案10】:

          对于使用 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);  
          }
          

          【讨论】:

            【解决方案11】:

            我遇到了两个方面的问题:
            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
            });
            

            希望这会有所帮助。

            【讨论】:

            • 这个答案对我来说效果最好,不会破坏可访问性/标签索引。
            【解决方案12】:

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

            <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/

            【讨论】:

              【解决方案13】:

              这对我使用 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 演示的 Fork:http://jsfiddle.net/163cwdrw/

              【讨论】:

                【解决方案14】:

                我在 select2 版本 3.4.8 中尝试了这些解决方案,发现当您执行 blur 时,select2 先触发 select2-close 然后 select2-focus 然后 select2-blur,所以最后我们最终会永远重新打开选择2。

                那么,我的解决方案是这个:

                $('#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)
                })
                

                【讨论】:

                  【解决方案15】:

                  不知何故 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')
                  

                  【讨论】:

                    【解决方案16】:

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

                        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');
                            }
                        });
                    

                    【讨论】:

                      【解决方案17】:

                      你可以用这个:

                       $(document).on('select2:open', () => {
                          document.querySelector('.select2-search__field').focus();
                        });
                      

                      【讨论】:

                        猜你喜欢
                        • 2015-10-04
                        • 2021-06-23
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-01-13
                        • 2012-05-10
                        • 2016-01-12
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多