web-dev-qa-db-ja.com

Select2フォーカスのドロップダウンを開く

複数のテキスト入力といくつかのselect2要素を持つフォームがあります。キーボードを使用してフィールド間をタブで移動すると正常に動作します-Select2要素はフォーム要素のように動作し、タブ移動時にフォーカスを受け取ります。 Select2要素がフォーカスを取得したときにドロップダウンを開くことが可能かどうか疑問に思っていました。

これまでに試したことは次のとおりです。

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

ただし、このコードを使用すると、選択後にドロップダウンが再び開きます。

26
andreivictor

V4.0 +の作業コード *(4.0.7を含む)

次のコードは、initialフォーカスでメニューを開きますが、メニューが閉じた後に選択が再びフォーカスされると、無限ループに陥りません。

_// 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イベントは2回発生

  1. フィールドにタブで移動するときに1回
  2. 再びフォーカスを復元するために開いたドロップダウンでタブ移動するとき

focus menu states

フォーカスイベントのタイプ間の違いを探すことで、無限ループを防ぐことができます。コントロールへの最初のフォーカスでのみメニューを開きたいので、次の発生したイベントを何らかの方法で区別する必要があります。

event timeline

ブラウザーが異なるイベントとともに異なる情報を送信し、Select2が以前のフローを中断するイベントの内部起動に多くの小さな変更を加えたため、クロスブラウザーフレンドリーな方法でこれを行うのは困難です。

動作するように思われる1つの方法は、メニューの closing イベント中にイベントハンドラーをアタッチし、それを使用して差し迫ったfocusイベントをキャプチャし、バブリングを防ぐことです。 DOMをアップします。次に、委任されたリスナーを使用して、focusイベントがdocumentに達するまでのみ、実際のフォーカス->オープンコードを呼び出します。

無効な選択を開かないようにする

このgithubの問題 #4025-タブフォーカスでドロップダウンが開かない で述べたように、次のような_'open'_ select要素で_:enabled_のみを呼び出すことを確認する必要があります。

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

Select2 DOMトラバーサル

DOMを少し横断する必要があるため、Select2によって生成されたHTML構造のマップを次に示します。

Select2 DOM

GitHubのソースコード

関連するコードセクションの一部を以下に示します。

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

以前はselect2を開くと2回起動していましたが、 Issue#35 で修正され、ジャンクを防ぐ必要がありました

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でテスト済み

29
KyleMit

バージョン3.5.4 の場合 (2015年8月30日以前)

現在の回答は、select2が起動したバージョン3.5.4以前にのみ適用されます。この場合、 ぼかしイベントとフォーカスイベントselect2-focusselect2-blur)。 $.one を使用して1回限りの使用ハンドラーをアタッチし、最初のフォーカスをキャッチし、その後の使用のためにぼかし中に再アタッチします。

$('.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の回答を両方試してみましたが、両方の問題に遭遇しました(ドロップダウンを2回クリックする必要があり、Firefoxは「イベントが定義されていません」をスローします)。

2つの問題を解決するようで、まだ他の問題に遭遇していない解決策を見つけました。これは@ 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>
26
tonywchen

ページ上のすべてのselect2インスタンスで動作する簡単なもの。

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

PDATE:上記のコードはIE11/Select2 4.0.で適切に動作しないようです。

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

おそらく選択が行われた後、select2-focusイベントがトリガーされます。

私が見つけた唯一の方法は、select2-focusおよびselect2-blurイベントとjQuery one イベントハンドラ。

そのため、要素が最初にフォーカスを取得すると、select2が1回(1つのため)開かれ、要素がぼやけると、1つのイベントハンドラーが再びアタッチされます。

コード:

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

17
Irvin Dominin

少し遅れていますが、select2 4.0.0を使用してコードを共有します

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

Select2のバージョン4.xの代替ソリューションを次に示します。リスナーを使用してフォーカスイベントをキャッチし、選択を開くことができます。

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

実際の例を見つける here @tonywchenによって作成された例

6

問題は、内部フォーカスイベントが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

4
Tomas Molnar

KyleMitの答えは私にとってはうまくいきました(ありがとう!)ドロップダウンが閉じているとき、メインの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');
        });
    }
});
4
Douglas

Select2.full.jsバージョン4.0.3を使用している私にとっては、上記のソリューションはどれも本来の方法で動作していませんでした。そこで、上記のソリューションの組み合わせを作成しました。まず、Select2.full.jsを修正して、「Thomas Molnar」が答えたように、内部フォーカスとぼかしイベントを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);  
}
3
Markus1980Wien

これらの多くを試してみて、最終的にSelect2 4.0.1で動作する次のものを思いつきました。要素は<select>要素。

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

重要なことは、複数選択を常に開いたままにすることです。最も簡単な方法は、コードの「条件」で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/

2
rajeev

これは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/

1
Mark Salvadore

私には2つの問題がありました:
1。複数のselect2要素があるフォームでは、ドロップダウンはタブで開かず、スペースキーを押して開く必要があります
2。選択を行った後、tabindexは尊重されず、次の入力フィールドを手動でクリックする必要があります

通常の提案は機能していましたが、ライブラリスクリプトが通常のselectからselect2への変換を行っていたため、この初期化を制御できなかったため、独自のバージョンを思いつきました。

ここに私のために働いたコードがあります。

タブを開く

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

選択時にnextに移動

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

お役に立てれば。

1
Gaurav

どういうわけか、select2Focusは空の選択では動作せず、問題を理解できませんでした。したがって、フォーカスイベントの自動オープン取得のトリガー後に手動制御を追加しました。

ここにcoffeescriptがあります:

$("#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')
0
Priit

私はかなりい解決策を試みましたが、それは私の問題を修正しました。

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

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)
})
0
Davsket