Select2 открыть раскрывающийся список в фокусе

У меня есть форма с несколькими текстовыми вводами и некоторыми элементами select2. Использование клавиатуры для перехода между полями работает нормально - элемент Select2 ведет себя как элемент формы и получает фокус при переходе с помощью табуляции. Мне было интересно, можно ли открыть раскрывающийся список, когда элемент Select2 получает фокус.

Вот что я пробовал до сих пор:

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

Но использование этого кода заставляет раскрывающийся список снова открываться после того, как будет сделан выбор.


person andreivictor    schedule 08.01.2014    source источник


Ответы (17)


Рабочий код для 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 - Выпадающий список не открывается при фокусе вкладки, мы должны убедитесь, что мы вызываем 'open' только для таких :enabled элементов select:

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

Select2 обход DOM

Нам нужно немного пройти по DOM, поэтому вот карта структуры HTML, созданная Select2

Select2 DOM

Исходный код на GitHub

Вот некоторые из соответствующих разделов кода в игре:

.on('mousedown' ... _11 _
.on('toggle' ... .toggleDropdown()
.toggleDropdown ... .open()
_ 16_ ... .trigger('focus'
_ 18_ ... $selection.focus()

Раньше открытие select2 срабатывало дважды, но это было исправлено в проблеме №3503 и это должно предотвратить некоторую дрянь

Похоже, что PR # 5357 нарушил предыдущий код фокуса, который работал в 4.05.

Рабочая демонстрация в 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

person KyleMit    schedule 13.03.2018
comment
Спасибо тебе за это. У меня работает в Chrome, FF и Safari на Mac при быстром тестировании. - person mungojerie; 12.03.2019
comment
На самом деле это вызвало у меня ошибку. Или то, как мой код взаимодействует с этим, вызывает ошибку. Если я щелкну вне select2 в поле ввода текста, select2 закроется, а затем снова откроется. Хром. Select2 v4.0.7. - person mungojerie; 17.05.2019
comment
Если я настрою вашу скрипку на использование v4.0.7, она больше не будет работать. jsfiddle.net/nwLbqfa4 - person mungojerie; 17.05.2019
comment
Но! Возврат к Select2 v4.0.5 и коду Кайла все еще работает. Остерегайтесь v4.0.7, всем, кто хочет открыть select2 на фокусе! Он откроется, но никогда не закроется .... :) - person mungojerie; 17.05.2019
comment
Подтверждено - не работает на 4.0.7 (последняя версия на момент написания статьи). - person Matt Olson; 20.05.2019
comment
@MattOlson, нашел решение для 4.0.7, которое работает во всех версиях 4+ - person KyleMit; 30.05.2019
comment
@mungojerie, спасибо за диагностику. Должно быть решение. Проверьте это и дайте мне знать. - person KyleMit; 30.05.2019
comment
Кажется, что это нарушает индекс вкладок при попытке перейти через форму к следующему полю после ввода select2. - person ouija; 26.07.2020
comment
@ouija, ты можешь добавить демо с этим случаем? - person KyleMit; 27.07.2020
comment
Отлично работает в 4.0.13. Именно то, что я искал, чтобы сделать вкладку моего приложения дружественной после добавления Select2. Большое спасибо KyleMit. Если у меня были какие-либо жалобы, так это на то, что я не могу выбрать / выделить желаемый элемент, а затем нажать только вкладку. Мне нужно нажать Enter, чтобы выбрать элемент из раскрывающегося списка. - person gecclesinc; 12.11.2020
comment
Как насчет нескольких? Не работает на нескольких. - person kodfire; 07.05.2021

Для версии 3.5.4 (30 августа 2015 г. и ранее)

Текущий ответ применим только к версиям 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 и фрагменты стека

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

person tonywchen    schedule 05.03.2014
comment
для новой версии 4.0.0 вам необходимо обновить события и их цели. - person Anas; 27.05.2015
comment
демо не работает. Пытался обновить его до версии 4.0, но все еще не работает: jsfiddle.net/brunodd/c7kvsu3o/5 < / а> - person brunodd; 15.10.2015

Что-то простое, что работало бы со всеми экземплярами 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');
    });
}
person rain01    schedule 31.07.2015
comment
Это сработало отлично, хотя похоже, что недавнее обновление IE11 сломало его :) - person Matthew Evans; 08.08.2018

Вероятно, после того, как выбор сделан, запускается select2-focus событие.

Единственный способ, который я нашел, - это комбинация событий select2-focus и select2-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/

ОБНОВИТЬ

Чтобы щелчок мыши работал, вы должны проверить событие, которое запускает обработчик, он должен запускать 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/

person Irvin Dominin    schedule 08.01.2014
comment
Спасибо за помощь. Это решение отлично подходит для навигации с помощью клавиатуры. Но я обнаружил ошибку: при щелчке мышью по элементу select2 требуется два щелчка мышью, чтобы открыть раскрывающийся список: первый щелчок приведет к фокусировке на элементе, а второй фактически откроет раскрывающийся список. - person andreivictor; 08.01.2014
comment
Я тестировал в хроме: работает нормально. В mozilla я получаю следующую ошибку: «событие не определено». - person andreivictor; 08.01.2014
comment
Я немного искал события firefox, это кажется сложной проблемой. В любом случае, большое спасибо за вашу помощь. - person andreivictor; 09.01.2014
comment
@andreivictor рад помочь вам, но я хочу найти способ решить эту проблему (для галочки тоже :-) - person Irvin Dominin; 09.01.2014
comment
Пример не работает. Пытался обновить до версии 4.0, но все еще не работает: jsfiddle.net/brunodd/fnjNb/71 - person brunodd; 15.10.2015
comment
@brunodd select2 4.0 отказался от поддержки событий: github.com/select2/select2/issues/1908, я ищу обходной путь - person Irvin Dominin; 15.10.2015
comment
Спасибо @IrvinDominin. Вот что я использую: jsfiddle.net/brunodd/c7kvsu3o/9 - person brunodd; 15.10.2015
comment
Однако по какой-то причине не работает IE11 = (Думаю, это может быть связано с самим select2 github .com / select2 / select2 / issues / 3300 - person brunodd; 15.10.2015
comment
Я попробовал последнюю скрипку в текущей версии Chrome, и меню выбора не открывалось в фокусе. - person neanderslob; 18.03.2016
comment
Итак, я обнаружил, что правило должно заключаться в том, чтобы открывать фокус, если фокус или размытие не произошло недавно. У меня есть небольшая библиотека управления потоком, которая упрощает кодирование такой логики с помощью сопрограмм. Вот демонстрация jsfiddle.net/xk5Lyfn0/1 - person George Mauer; 19.10.2017

немного поздно ... но поделиться своим кодом с помощью select2 4.0.0

$("#my_id").select2();
$("#my_id").next(".select2").find(".select2-selection").focus(function() {
    $("#my_id").select2("open");
});
person Claudio Ikeda    schedule 22.06.2015
comment
Это решение не работает в IE = / Он открывается, но никогда не закрывается (даже не щелкая снаружи, табуляция). Протестировано в IE 11 - person cr0ss; 14.10.2015
comment
Это работает, когда вы выбираете отдельный элемент по идентификатору, но если вы хотите настроить таргетинг на несколько полей select2 (а не открывать каждый раз только первое поле), это работает: $('.my-class').next('.select2').find('.select2-selection').focus(function (e) { $(this).closest('.select2').prev('select..my-class').select2('open'); }); (Извините, по-видимому, не может быть разрывов строк в комментариях.) - person hackel; 20.11.2015
comment
Спасибо, чувак, между старой и новой версиями вся информация перепуталась. У меня работает, просто .select2 (open) делает свое дело. - person Obed Marquez Parlapiano; 17.07.2017

Вот альтернативное решение для версии 4.x Select2. Вы можете использовать слушателей, чтобы поймать событие фокуса, а затем открыть выбор.

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

Найдите рабочий пример здесь на основе экзамена, созданного @tonywchen

person Mathieu de Lorimier    schedule 20.06.2016

Ответ 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');
        });
    }
});
person Douglas    schedule 25.05.2018
comment
Привет, @Douglas, у меня проблемы с воспроизведением проблемы, которую вы здесь рассматриваете, но 4 отзыва говорят о том, что другие люди, вероятно, сталкивались с ней. Когда вы видите, что порядок табуляции теряется? - person KyleMit; 23.04.2019
comment
Этот тоже работает на нескольких. Спасибо. - person kodfire; 07.05.2021

Проблема в том, что событие внутреннего фокуса не преобразуется в событие jQuery, поэтому я изменил плагин и добавил событие фокуса в EventRelay в строке 2063 Select2 4.0.3:

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

person Tomas Molnar    schedule 14.10.2016

Я попробовал несколько из них и, наконец, пришел к следующему, который у меня работает с Select2 4.0.1. element - это элемент <select>.

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

Для меня, использующего Select2.full.js версии 4.0.3, ни одно из вышеперечисленных решений не работало должным образом. Итак, я написал комбинацию вышеперечисленных решений. Прежде всего, я изменил Select2.full.js, чтобы перенести события внутреннего фокуса и размытия в события jquery, как это сделал Томас Мольнар в своем ответе.

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);  
}
person Markus1980Wien    schedule 08.01.2017

У меня была двоякая проблема:
1. В форме с несколькими элементами select2 раскрывающийся список не открывается на вкладке, и вам нужно нажать клавишу пробела, чтобы открыть его
2. Как только вы сделали выбор, tabindex не будет учитываться, и вам придется вручную щелкнуть следующее поле ввода

Хотя обычные предложения сработали, я придумал свою собственную версию, поскольку скрипт библиотеки выполнял преобразование обычного select в 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
});

Надеюсь это поможет.

person Gaurav    schedule 20.06.2017
comment
Этот ответ сработал для меня лучше всего, не нарушая индекс доступности / вкладки. - person ouija; 26.07.2020

Важно, чтобы множественный выбор был открыт все время. Самый простой способ - запустить событие open для «условий» в коде:

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

person rajeev    schedule 18.04.2018

Это сработало для меня, используя 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();

Вилка демонстрации Ирвина Доминина: http://jsfiddle.net/163cwdrw/

person Mark Salvadore    schedule 21.03.2017

Я попробовал эти решения с 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)
})
person Davsket    schedule 06.05.2014

Каким-то образом 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')
person Priit    schedule 06.06.2014

Я пробовал довольно уродливое решение, но оно устранило мою проблему.

    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');
        }
    });
person wmwmwm    schedule 18.05.2016

Вы можете использовать это:

 $(document).on('select2:open', () => {
    document.querySelector('.select2-search__field').focus();
  });
person Hammad Ahmed khan    schedule 03.05.2021