web-dev-qa-db-ja.com

タブインデックスの次の要素にフォーカス

フォーカスがある現在の要素に基づいて、タブシーケンス内の次の要素にフォーカスを移動しようとしています。これまでのところ、検索で何も表示していません。

function OnFocusOut()
{
    var currentElement = $get(currentElementId); // ID set by OnFocusIn 

    currentElementId = "";
    currentElement.nextElementByTabIndex.focus();
}

もちろん、nextElementByTabIndexは、これが機能するための重要な部分です。タブシーケンスの次の要素を見つけるにはどうすればよいですか?ソリューションは、JQueryのようなものではなく、JScriptを使用する必要があります。

80
JadziaMD

Jqueryを使用しない場合:まず、タブ可能な要素にclass="tabable"を追加します。これにより、後で選択できるようになります。 (以下のコードの「。」クラスセレクタプレフィックスを忘れないでください)

var lastTabIndex = 10;
function OnFocusOut()
{
    var currentElement = $get(currentElementId); // ID set by OnFOcusIn
    var curIndex = currentElement.tabIndex; //get current elements tab index
    if(curIndex == lastTabIndex) { //if we are on the last tabindex, go back to the beginning
        curIndex = 0;
    }
    var tabbables = document.querySelectorAll(".tabable"); //get all tabable elements
    for(var i=0; i<tabbables.length; i++) { //loop through each element
        if(tabbables[i].tabIndex == (curIndex+1)) { //check the tabindex to see if it's the element we want
            tabbables[i].focus(); //if it's the one we want, focus it and exit the loop
            break;
        }
    }
}
19
Brian Glaz

私はこれを実装したことはありませんが、私は同様の問題を調べました。

最初にこれを試してください

最初に、現在フォーカスがある要素のTabキーに対して keypressイベントを発生させる ができるかどうかを確認します。ブラウザごとにこれを行う別の方法があるかもしれません。

それがうまくいかない場合は、一生懸命働く必要があります…

JQuery実装を参照するには、以下を行う必要があります。

  1. TabとShift + Tabをリッスンします
  2. どの要素がタブ可能かを知る
  3. タブオーダーの仕組みを理解する

1. TabとShift + Tabを聞きます

TabおよびShift + Tabのリッスンは、おそらくWebの他の場所で十分にカバーされているので、その部分はスキップします。

2.どの要素がタブ可能かを知る

どの要素がタブ化可能であるかを知ることは、より複雑です。基本的に、要素はフォーカス可能で、属性tabindex="-1"が設定されていない場合、タブ可能です。そのため、どの要素がフォーカス可能かを尋ねる必要があります。次の要素はフォーカス可能です。

  • 無効化されていないinputselecttextareabutton、およびobject要素。
  • aまたはareaの数値が設定されているhrefおよびtabindex要素。
  • tabindexの数値が設定されている要素。

さらに、要素は次の場合にのみフォーカス可能です。

  • その祖先はdisplay: noneではありません。
  • visibilityの計算値はvisibleです。つまり、visibilityを設定する最も近い祖先の値はvisibleでなければなりません。祖先にvisibilityが設定されていない場合、計算される値はvisibleです。

詳細は別の Stack Overflow answer にあります。

3.タブオーダーの仕組みを理解する

文書内の要素のタブ順序は、tabindex属性によって制御されます。値が設定されていない場合、tabindexは実質的に0です。

ドキュメントのtabindex順序は、1、2、3、…、0です。

最初に、body要素(または要素なし)にフォーカスがある場合、タブオーダーの最初の要素は、ゼロ以外の最小のtabindexです。複数の要素が同じtabindexを持っている場合、そのtabindexを持つ最後の要素に到達するまでドキュメントの順序で進みます。次に、次に低いtabindexに移動し、プロセスを続行します。最後に、これらの要素をゼロ(または空)tabindexで終了します。

61
Chris Calo

以下は、この目的のために作成したものです。

focusNextElement: function () {
    //add all elements we want to include in our selection
    var focussableElements = 'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])';
    if (document.activeElement && document.activeElement.form) {
        var focussable = Array.prototype.filter.call(document.activeElement.form.querySelectorAll(focussableElements),
        function (element) {
            //check for visibility while always include the current activeElement 
            return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement
        });
        var index = focussable.indexOf(document.activeElement);
        if(index > -1) {
           var nextElement = focussable[index + 1] || focussable[0];
           nextElement.focus();
        }                    
    }
}

特徴:

  • フォーカス可能な要素の構成可能なセット
  • jQueryは不要
  • 最新のすべてのブラウザで動作します
  • 高速で軽量
24
Mx.

シンプルなjQueryプラグイン を作成しました。 jQuery UIの「:tabbable」セレクターを使用して、次の「tabbable」要素を見つけて選択します。

使用例:

// Simulate tab key when element is clicked 
$('.myElement').bind('click', function(event){
    $.tabNext();
    return false;
});
22
Mark Lagendijk

答えの中核は、次の要素を見つけることにあります。

  function findNextTabStop(el) {
    var universe = document.querySelectorAll('input, button, select, textarea, a[href]');
    var list = Array.prototype.filter.call(universe, function(item) {return item.tabIndex >= "0"});
    var index = list.indexOf(el);
    return list[index + 1] || list[0];
  }

使用法:

var nextEl = findNextTabStop(element);
nextEl.focus();

tabIndexの優先順位を気にしないことに注意してください。

6
André Werlang

上記のコメントで述べたように、ブラウザがタブ順序情報を公開することはないと思います。ここでは、タブオーダーで次の要素を取得するためにブラウザーが実行する処理の簡略化された近似を示します。

var allowedTags = {input: true, textarea: true, button: true};

var walker = document.createTreeWalker(
  document.body,
  NodeFilter.SHOW_ELEMENT,
  {
    acceptNode: function(node)
    {
      if (node.localName in allowedTags)
        return NodeFilter.FILTER_ACCEPT;
      else
        NodeFilter.FILTER_SKIP;
    }
  },
  false
);
walker.currentNode = currentElement;
if (!walker.nextNode())
{
  // Restart search from the start of the document
  walker.currentNode = walker.root;
  walker.nextNode();
}
if (walker.currentNode && walker.currentNode != walker.root)
  walker.currentNode.focus();

これはいくつかのタグのみを考慮し、tabindex属性を無視しますが、何を達成しようとしているかによっては十分かもしれません。

2
Wladimir Palant

要素のtabIndexプロパティをチェックして、フォーカス可能であるかどうかを判断できるようです。フォーカス可能でない要素のtabindexの値は「-1」です。

次に、タブストップのルールを知る必要があります:

  • tabIndex="1"の優先度が最も高くなっています。
  • tabIndex="2"が次に高い優先度を持ちます。
  • tabIndex="3"が次に続きます。
  • tabIndex="0"(またはデフォルトでタブ移動可能)の優先度は最低です。
  • tabIndex="-1"(またはデフォルトではタブ移動不可)はタブストップとして機能しません。
  • 同じtabIndexを持つ2つの要素の場合、DOMで最初に表示される要素の優先順位が高くなります。

純粋なJavascriptを使用して、タブストップのリストを順番に作成する方法の例を次に示します。

function getTabStops(o, a, el) {
    // Check if this element is a tab stop
    if (el.tabIndex > 0) {
        if (o[el.tabIndex]) {
            o[el.tabIndex].Push(el);
        } else {
            o[el.tabIndex] = [el];
        }
    } else if (el.tabIndex === 0) {
        // Tab index "0" comes last so we accumulate it seperately
        a.Push(el);
    }
    // Check if children are tab stops
    for (var i = 0, l = el.children.length; i < l; i++) {
        getTabStops(o, a, el.children[i]);
    }
}

var o = [],
    a = [],
    stops = [],
    active = document.activeElement;

getTabStops(o, a, document.body);

// Use simple loops for maximum browser support
for (var i = 0, l = o.length; i < l; i++) {
    if (o[i]) {
        for (var j = 0, m = o[i].length; j < m; j++) {
            stops.Push(o[i][j]);
        }
    }
}
for (var i = 0, l = a.length; i < l; i++) {
    stops.Push(a[i]);
}

最初にDOMを調べて、すべてのタブストップをインデックスと順番に収集します。次に、最終リストを作成します。 tabIndexが1、2、3などのアイテムの後に、リストの最後にtabIndex="0"のアイテムを追加することに注意してください。

「Enter」キーを使用してタブ移動できる完全に機能する例については、この fiddle を確認してください。

2
chowey

上記の解決策を確認しましたが、かなり時間がかかりました。これは、たった1行のコードで実現できます。

currentElement.nextElementSibling.focus();

または

currentElement.previousElementSibling.focus();

ここで、currentElementはdocument.activeElementのいずれか、または現在の要素が関数のコンテキストにある場合はthisになります。

キーダウンイベントでタブイベントとシフトタブイベントを追跡しました

let cursorDirection = ''
$(document).keydown(function (e) {
    let key = e.which || e.keyCode;
    if (e.shiftKey) {
        //does not matter if user has pressed tab key or not.
        //If it matters for you then compare it with 9
        cursorDirection = 'prev';
    }
    else if (key == 9) {
        //if tab key is pressed then move next.
        cursorDirection = 'next';
    }
    else {
        cursorDirection == '';
    }
});

カーソルの方向が決まったら、nextElementSibling.focusまたはpreviousElementSibling.focusメソッドを使用できます

Tabbable は、すべてのタブ可能な要素のリストをタブ順で提供する小さなJSパッケージです。そのため、そのリスト内で要素を見つけて、次のリストエントリに焦点を当てることができます。

このパッケージは、他の回答に記載されている複雑なEdgeのケースを正しく処理します(たとえば、祖先がdisplay: noneになることはありません)。そして、jQueryに依存しません!

この記事の執筆時点(バージョン1.1.1)では、IE8をサポートしておらず、ブラウザーのバグによりcontenteditableを正しく処理できないという警告があります。

1
Nate Sullivan

これがSOの最初の投稿なので、受け入れられた答えにコメントするほどの評判はありませんが、コードを次のように変更する必要がありました。

export function focusNextElement () {
  //add all elements we want to include in our selection
  const focussableElements = 
    'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled])'
  if (document.activeElement && document.activeElement.form) {
      var focussable = Array.prototype.filter.call(
        document.activeElement.form.querySelectorAll(focussableElements),
      function (element) {
          // if element has tabindex = -1, it is not focussable
          if ( element.hasAttribute('tabindex') && element.tabIndex === -1 ){
            return false
          }
          //check for visibility while always include the current activeElement 
          return (element.offsetWidth > 0 || element.offsetHeight > 0 || 
            element === document.activeElement)
      });
      console.log(focussable)
      var index = focussable.indexOf(document.activeElement);
      if(index > -1) {
         var nextElement = focussable[index + 1] || focussable[0];
         console.log(nextElement)
         nextElement.focus()
      }                    
  }
}

Varを定数に変更することは重要ではありません。主な変更点は、tabindex!= "-1"をチェックするセレクターを取り除くことです。その後、要素にtabindex属性があり、かつ「-1」に設定されている場合、フォーカス可能とは見なされません。

これを変更する必要がある理由は、tabindex = "-1"を<input>に追加するときに、この要素が「input [type = text]:not([disabled])」と一致するため、フォーカス可能と見なされたためです。セレクタ。私の変更は、「無効化されていないテキスト入力であり、tabIndex属性があり、その属性の値が-1である場合、フォーカス可能と見なされるべきではない」と同等です。

受け入れられた回答の作成者がtabIndex属性を説明するために回答を編集したときに、正しく編集しなかったと思います。そうでない場合はお知らせください

1
BrushyAmoeba

循環させる各要素に独自のtabIndex値を指定しましたか?もしそうなら、あなたはこれを試すことができます:

var lasTabIndex = 10; //Set this to the highest tabIndex you have
function OnFocusOut()
{
    var currentElement = $get(currentElementId); // ID set by OnFocusIn 

    var curIndex = $(currentElement).attr('tabindex'); //get the tab index of the current element
    if(curIndex == lastTabIndex) { //if we are on the last tabindex, go back to the beginning
        curIndex = 0;
    }
    $('[tabindex=' + (curIndex + 1) + ']').focus(); //set focus on the element that has a tab index one greater than the current tab index
}

Jqueryを使用していますよね?

0
Brian Glaz
function focusNextElement(){
  var focusable = [].slice.call(document.querySelectorAll("a, button, input, select, textarea, [tabindex], [contenteditable]")).filter(function($e){
    if($e.disabled || ($e.getAttribute("tabindex") && parseInt($e.getAttribute("tabindex"))<0)) return false;
    return true;
  }).sort(function($a, $b){
    return (parseFloat($a.getAttribute("tabindex") || 99999) || 99999) - (parseFloat($b.getAttribute("tabindex") || 99999) || 99999);
  });
  var focusIndex = focusable.indexOf(document.activeElement);
  if(focusable[focusIndex+1]) focusable[focusIndex+1].focus();
};
0
Dustin Poissant

これは、@ Kanoおよび@ Mxが提供する優れたソリューションに対する潜在的な拡張機能です。 TabIndexの順序を保持する場合は、このソートを中央に追加します。

// Sort by explicit Tab Index, if any
var sort_by_TabIndex = function (elementA, elementB) {
    let a = elementA.tabIndex || 1;
    let b = elementB.tabIndex || 1;
    if (a < b) { return -1; }
    if (a > b) { return 1; }
    return 0;
}
focussable.sort(sort_by_TabIndex);
0
DavB.cs

次は、次の要素に焦点を当てたより完全なバージョンです。仕様のガイドラインに従い、tabindexを使用して要素のリストを正しく並べ替えます。また、前の要素を取得する場合は、逆変数が定義されます。

function focusNextElement( reverse, activeElem ) {
  /*check if an element is defined or use activeElement*/
  activeElem = activeElem instanceof HTMLElement ? activeElem : document.activeElement;

  let queryString = [
      'a:not([disabled]):not([tabindex="-1"])',
      'button:not([disabled]):not([tabindex="-1"])',
      'input:not([disabled]):not([tabindex="-1"])',
      'select:not([disabled]):not([tabindex="-1"])',
      '[tabindex]:not([disabled]):not([tabindex="-1"])'
      /* add custom queries here */
    ].join(','),
    queryResult = Array.prototype.filter.call(document.querySelectorAll(queryString), elem => {
      /*check for visibility while always include the current activeElement*/
      return elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem === activeElem;
    }),
    indexedList = queryResult.slice().filter(elem => {
      /* filter out all indexes not greater than 0 */
      return elem.tabIndex == 0 || elem.tabIndex == -1 ? false : true;
    }).sort((a, b) => {
      /* sort the array by index from smallest to largest */
      return a.tabIndex != 0 && b.tabIndex != 0 
        ? (a.tabIndex < b.tabIndex ? -1 : b.tabIndex < a.tabIndex ? 1 : 0) 
        : a.tabIndex != 0 ? -1 : b.tabIndex != 0 ? 1 : 0;
    }),
    focusable = [].concat(indexedList, queryResult.filter(elem => {
      /* filter out all indexes above 0 */
      return elem.tabIndex == 0 || elem.tabIndex == -1 ? true : false;
    }));

  /* if reverse is true return the previous focusable element
     if reverse is false return the next focusable element */
  return reverse ? (focusable[focusable.indexOf(activeElem) - 1] || focusable[focusable.length - 1]) 
    : (focusable[focusable.indexOf(activeElem) + 1] || focusable[0]);
}
0
svarlitskiy

これが役に立てば幸いです。

<input size="2" tabindex="1" id="one"
  maxlength="2" onkeyup="toUnicode(this)" />

<input size="2" tabindex="2" id="two"
  maxlength="2" onkeyup="toUnicode(this)" />

<input size="2" tabindex="3" id="three"
 maxlength="2" onkeyup="toUnicode(this)" />

次に、シンプルなjavascriptを使用します

function toUnicode(elmnt)
{
  var next;
 if (elmnt.value.length==elmnt.maxLength)
{
next=elmnt.tabIndex + 1;
//look for the fields with the next tabIndex
var f = elmnt.form;
for (var i = 0; i < f.elements.length; i++)
{
  if (next<=f.elements[i].tabIndex)
  {
    f.elements[i].focus();
    break;
    }
   }
  }
}
0
Shafiqul Islam

これを呼び出すことができます:

タブ:

$.tabNext();

Shift + Tab:

$.tabPrev();
<!DOCTYPE html>
<html>
<body>
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
<script>
(function($){
        'use strict';

        /**
         * Focusses the next :focusable element. Elements with tabindex=-1 are focusable, but not tabable.
         * Does not take into account that the taborder might be different as the :tabbable elements order
         * (which happens when using tabindexes which are greater than 0).
         */
        $.focusNext = function(){
                selectNextTabbableOrFocusable(':focusable');
        };

        /**
         * Focusses the previous :focusable element. Elements with tabindex=-1 are focusable, but not tabable.
         * Does not take into account that the taborder might be different as the :tabbable elements order
         * (which happens when using tabindexes which are greater than 0).
         */
        $.focusPrev = function(){
                selectPrevTabbableOrFocusable(':focusable');
        };

        /**
         * Focusses the next :tabable element.
         * Does not take into account that the taborder might be different as the :tabbable elements order
         * (which happens when using tabindexes which are greater than 0).
         */
        $.tabNext = function(){
                selectNextTabbableOrFocusable(':tabbable');
        };

        /**
         * Focusses the previous :tabbable element
         * Does not take into account that the taborder might be different as the :tabbable elements order
         * (which happens when using tabindexes which are greater than 0).
         */
        $.tabPrev = function(){
                selectPrevTabbableOrFocusable(':tabbable');
        };

    function tabIndexToInt(tabIndex){
        var tabIndexInded = parseInt(tabIndex);
        if(isNaN(tabIndexInded)){
            return 0;
        }else{
            return tabIndexInded;
        }
    }

    function getTabIndexList(elements){
        var list = [];
        for(var i=0; i<elements.length; i++){
            list.Push(tabIndexToInt(elements.eq(i).attr("tabIndex")));
        }
        return list;
    }

    function selectNextTabbableOrFocusable(selector){
        var selectables = $(selector);
        var current = $(':focus');

        // Find same TabIndex of remainder element
        var currentIndex = selectables.index(current);
        var currentTabIndex = tabIndexToInt(current.attr("tabIndex"));
        for(var i=currentIndex+1; i<selectables.length; i++){
            if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === currentTabIndex){
                selectables.eq(i).focus();
                return;
            }
        }

        // Check is last TabIndex
        var tabIndexList = getTabIndexList(selectables).sort(function(a, b){return a-b});
        if(currentTabIndex === tabIndexList[tabIndexList.length-1]){
            currentTabIndex = -1;// Starting from 0
        }

        // Find next TabIndex of all element
        var nextTabIndex = tabIndexList.find(function(element){return currentTabIndex<element;});
        for(var i=0; i<selectables.length; i++){
            if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === nextTabIndex){
                selectables.eq(i).focus();
                return;
            }
        }
    }

        function selectPrevTabbableOrFocusable(selector){
                var selectables = $(selector);
                var current = $(':focus');

                // Find same TabIndex of remainder element
        var currentIndex = selectables.index(current);
        var currentTabIndex = tabIndexToInt(current.attr("tabIndex"));
        for(var i=currentIndex-1; 0<=i; i--){
            if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === currentTabIndex){
                selectables.eq(i).focus();
                return;
            }
        }

        // Check is last TabIndex
        var tabIndexList = getTabIndexList(selectables).sort(function(a, b){return b-a});
        if(currentTabIndex <= tabIndexList[tabIndexList.length-1]){
            currentTabIndex = tabIndexList[0]+1;// Starting from max
        }

        // Find prev TabIndex of all element
        var prevTabIndex = tabIndexList.find(function(element){return element<currentTabIndex;});
        for(var i=selectables.length-1; 0<=i; i--){
            if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === prevTabIndex){
                selectables.eq(i).focus();
                return;
            }
        }
        }

        /**
         * :focusable and :tabbable, both taken from jQuery UI Core
         */
        $.extend($.expr[ ':' ], {
                data: $.expr.createPseudo ?
                        $.expr.createPseudo(function(dataName){
                                return function(elem){
                                        return !!$.data(elem, dataName);
                                };
                        }) :
                        // support: jQuery <1.8
                        function(elem, i, match){
                                return !!$.data(elem, match[ 3 ]);
                        },

                focusable: function(element){
                        return focusable(element, !isNaN($.attr(element, 'tabindex')));
                },

                tabbable: function(element){
                        var tabIndex = $.attr(element, 'tabindex'),
                                isTabIndexNaN = isNaN(tabIndex);
                        return ( isTabIndexNaN || tabIndex >= 0 ) && focusable(element, !isTabIndexNaN);
                }
        });

        /**
         * focussable function, taken from jQuery UI Core
         * @param element
         * @returns {*}
         */
        function focusable(element){
                var map, mapName, img,
                        nodeName = element.nodeName.toLowerCase(),
                        isTabIndexNotNaN = !isNaN($.attr(element, 'tabindex'));
                if('area' === nodeName){
                        map = element.parentNode;
                        mapName = map.name;
                        if(!element.href || !mapName || map.nodeName.toLowerCase() !== 'map'){
                                return false;
                        }
                        img = $('img[usemap=#' + mapName + ']')[0];
                        return !!img && visible(img);
                }
                return ( /^(input|select|textarea|button|object)$/.test(nodeName) ?
                        !element.disabled :
                        'a' === nodeName ?
                                element.href || isTabIndexNotNaN :
                                isTabIndexNotNaN) &&
                        // the element and all of its ancestors must be visible
                        visible(element);

                function visible(element){
                        return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function(){
                                return $.css(this, 'visibility') === 'hidden';
                        }).length;
                }
        }
})(jQuery);
</script>

<a tabindex="5">5</a><br>
<a tabindex="20">20</a><br>
<a tabindex="3">3</a><br>
<a tabindex="7">7</a><br>
<a tabindex="20">20</a><br>
<a tabindex="0">0</a><br>

<script>
var timer;
function tab(){
    window.clearTimeout(timer)
    timer = window.setInterval(function(){$.tabNext();}, 1000);
}
function shiftTab(){
    window.clearTimeout(timer)
    timer = window.setInterval(function(){$.tabPrev();}, 1000);
}
</script>
<button tabindex="-1" onclick="tab()">Tab</button>
<button tabindex="-1" onclick="shiftTab()">Shift+Tab</button>

</body>
</html>

jquery.tabbable PlugInを変更して完了します。

0
iHad 169