Nächstes Element im Registerkartenindex fokussieren

103

Ich versuche, den Fokus auf das nächste Element in der Registerkartenfolge zu verschieben, basierend auf dem aktuellen Element, das den Fokus hat. Bisher habe ich bei meinen Suchen nichts gefunden.

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

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

Natürlich ist der nextElementByTabIndex der Schlüssel, damit dies funktioniert. Wie finde ich das nächste Element in der Tabulatorsequenz? Die Lösung müsste auf JScript basieren und nicht auf JQuery.

JadziaMD
quelle
3
Warum hast du diese Linie currentElementId = "";?
1
Ich glaube nicht, dass Browser die Informationen zur Tab-Reihenfolge offenlegen - und der von den Browsern selbst verwendete Algorithmus ist zu kompliziert, um repliziert zu werden. Vielleicht können Sie Ihre Anforderungen, zB „betrachten nur einschränken input, buttonund textareaTags und ignorieren tabindexAttribut“.
Wladimir Palant
Wir müssen Ihren .newElementByTabIndexCode sehen, weil das nicht funktioniert.
0x499602D2
2
Andererseits ist die Beschränkung auf bestimmte Tags möglicherweise nicht erforderlich - man kann überprüfen, ob die focus()Methode vorhanden ist.
Wladimir Palant
1
@ David Das ist die Funktion, die es nicht gibt, daher meine Frage. : D
JadziaMD

Antworten:

23

Ohne Abfrage: Fügen Sie zunächst Ihre tabulatorfähigen Elemente hinzu class="tabable", damit wir sie später auswählen können. (Vergessen Sie nicht das Präfix der Klassenauswahl "." Im folgenden Code.)

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;
        }
    }
}
Brian Glaz
quelle
16
Eine Lösung, ohne jedem Element einen Namen hinzufügen zu müssen (da es viele gibt, die möglich sind), wäre ideal.
JadziaMD
3
ok, ist das für ein Formular? Wenn alle gewünschten Elemente Eingabeelemente sind, können Sie stattdessen die Zeile var tabbables = document.getElementsByName("tabable");durch ersetzenvar tabbables = document.getElementsByTagName("input");
Brian Glaz
var tabbables = document.querySelectorAll("input, textarea, button")// IE8 +, erhalte einen Verweis auf alle Tabellen, ohne deinen HTML-Code zu ändern.
Greg
2
class = "tabbable" anstatt das Namensattribut zu verwenden
Chris F Carroll
4
Beachten Sie, dass bei Verwendung von Flexbox die Reihenfolge der Elemente im DOM anders ist als im Browser. Das Auswählen des nächsten tabellarischen Elements funktioniert nicht, wenn Sie die Reihenfolge der Elemente mithilfe der Flexbox ändern.
Haneev
75

Ich habe dies nie implementiert, aber ich habe ein ähnliches Problem untersucht, und hier ist, was ich versuchen würde.

Versuchen Sie dies zuerst

Zuerst würde ich sehen, ob Sie einfach ein keypressEreignis für die Tabulatortaste auf das Element auslösen könnten , das derzeit den Fokus hat. Es kann verschiedene Möglichkeiten geben, dies für verschiedene Browser zu tun.

Wenn das nicht funktioniert, müssen Sie härter arbeiten ...

Wenn Sie auf die jQuery-Implementierung verweisen, müssen Sie:

  1. Hören Sie auf Tab und Umschalt + Tab
  2. Wissen, welche Elemente tabulatorfähig sind
  3. Verstehen Sie, wie die Tab-Reihenfolge funktioniert

1. Achten Sie auf Tab und Umschalt + Tab

Das Abhören von Tab und Shift + Tab wird wahrscheinlich an anderer Stelle im Web gut behandelt, daher überspringe ich diesen Teil.

2. Wissen, welche Elemente tabulatorfähig sind

Zu wissen, welche Elemente tabulatorfähig sind, ist schwieriger. Grundsätzlich ist ein Element tabulatorfähig, wenn es fokussierbar ist und das Attribut nicht tabindex="-1"festgelegt ist. Dann müssen wir uns fragen, welche Elemente fokussierbar sind. Die folgenden Elemente sind fokussierbar:

  • input, select, textarea, button, Und objectElemente , die nicht deaktiviert werden.
  • aund areaElemente, die einen hrefoder einen numerischen Wert für tabindexset haben.
  • Jedes Element, das einen numerischen Wert für tabindexset hat.

Darüber hinaus ist ein Element nur fokussierbar, wenn:

  • Keiner seiner Vorfahren ist display: none.
  • Der berechnete Wert von visibilityist visible. Dies bedeutet, dass der nächste zu setzende Vorfahr visibilityeinen Wert von haben muss visible. Wenn kein Vorfahr visibilityfestgelegt wurde, lautet der berechnete Wert visible.

Weitere Details finden Sie in einer anderen Antwort zum Stapelüberlauf .

3. Verstehen Sie, wie die Tab-Reihenfolge funktioniert

Die Tabulatorreihenfolge der Elemente in einem Dokument wird durch das tabindexAttribut gesteuert . Wenn kein Wert eingestellt ist, tabindexist das effektiv 0.

Die tabindexReihenfolge für das Dokument lautet: 1, 2, 3,…, 0.

Wenn das bodyElement (oder kein Element) den Fokus hat, ist das erste Element in der Registerkartenreihenfolge zunächst das niedrigste ungleich Null tabindex. Wenn mehrere Elemente dasselbe haben tabindex, gehen Sie in Dokumentreihenfolge, bis Sie damit das letzte Element erreichen tabindex. Dann bewegen Sie sich zum nächstniedrigeren tabindexund der Prozess wird fortgesetzt. Beenden Sie schließlich mit diesen Elementen mit einer Null (oder leer) tabindex.

Chris Calo
quelle
37

Hier ist etwas, das ich für diesen Zweck baue:

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

Eigenschaften:

  • konfigurierbarer Satz fokussierbarer Elemente
  • Keine jQuery erforderlich
  • funktioniert in allen modernen Browsern
  • schnell und leicht
Mx.
quelle
2
Dies ist die effektivste und ressourcenschonendste Lösung. Danke dir! Hier ist mein voll funktionsfähiges Skript: stackoverflow.com/a/40686327/1589669
eapo
Ich habe unten einen Ausschnitt hinzugefügt, um die Sortierung nach explizitem TabIndex focussable.sort (sort_by_TabIndex)
DavB.cs
1
Das beste ! Es muss so komplex sein: Das ist nextElementSiblingmöglicherweise nicht fokussierbar, das nächste fokussierbar ist möglicherweise kein Geschwister.
Tinmarino
Guter Ansatz, aber es sollte jede Eingabe zulassen, die nicht vom Typ ist hiddenund auch textareaund abdeckt select.
Lucero vor
23

Ich habe ein einfaches jQuery-Plugin erstellt, das genau dies tut. Es verwendet den Selektor ': tabbable' der jQuery-Benutzeroberfläche, um das nächste 'tabbable'-Element zu finden und auszuwählen.

Anwendungsbeispiel:

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

Der Kern der Antwort liegt darin, das nächste Element zu finden:

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

Verwendung:

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

Beachten Sie, dass mir das Priorisieren egal ist tabIndex.

André Werlang
quelle
3
Was ist, wenn die Tabindex-Bestellung gegen die Dokumentenbestellung verstößt? Ich denke, das Array muss nach Tabindex-Nummer und dann nach Dokumentreihenfolge sortiert werden
Chris F Carroll
Ja, das wäre "spezifikationskonformer". Ich bin mir nicht sicher über
Randfälle
Was ist, wenn ein Element, das nicht zu diesen Tags gehört, ein tabindex-Attribut hat?
Matt Pennington
1
@ MattPennington Es würde ignoriert. Der Filter ist (ein Versuch), die Suche zu beschleunigen, zögern Sie nicht, sich anzupassen.
André Werlang
3

Wie in einem Kommentar oben erwähnt, glaube ich nicht, dass Browser Informationen zur Tab-Reihenfolge anzeigen. Hier eine vereinfachte Annäherung an das, was der Browser tut, um das nächste Element in Tabulatorreihenfolge zu erhalten:

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

Dies berücksichtigt nur einige Tags und ignoriert tabindexAttribute, kann jedoch ausreichen, je nachdem, was Sie erreichen möchten.

Wladimir Palant
quelle
3

Es scheint, dass Sie die tabIndexEigenschaft eines Elements überprüfen können, um festzustellen, ob es fokussierbar ist. Ein Element, das nicht fokussierbar ist, hat ein tabindex"-1".

Dann müssen Sie nur noch die Regeln für Tabulatoren kennen:

  • tabIndex="1" hat die höchste Priorität.
  • tabIndex="2" hat die nächsthöhere Priorität.
  • tabIndex="3" ist als nächstes und so weiter.
  • tabIndex="0" (oder standardmäßig tabellierbar) hat die niedrigste Priorität.
  • tabIndex="-1" (oder standardmäßig nicht tabellierbar) fungiert nicht als Tabulator.
  • Für zwei Elemente mit demselben tabIndex hat das Element, das zuerst im DOM angezeigt wird, die höhere Priorität.

Hier ist ein Beispiel, wie Sie die Liste der Tabulatoren nacheinander mit reinem Javascript erstellen:

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

Wir gehen zuerst durch das DOM und sammeln alle Tabstopps nacheinander mit ihrem Index. Wir stellen dann die endgültige Liste zusammen. Beachten Sie, dass wir die Elemente mit ganz tabIndex="0"am Ende der Liste nach den Elementen mit einem tabIndexvon 1, 2, 3 usw. hinzufügen .

Ein voll funktionsfähiges Beispiel, in dem Sie mit der Eingabetaste herumblättern können, finden Sie in dieser Geige .

Chowey
quelle
2

Tabbable ist ein kleines JS-Paket, mit dem Sie eine Liste aller tabellarischen Elemente in Tab-Reihenfolge erhalten . So können Sie Ihr Element in dieser Liste finden und sich dann auf den nächsten Listeneintrag konzentrieren.

Das Paket behandelt die komplizierten Randfälle, die in anderen Antworten erwähnt werden, korrekt (z. B. kann kein Vorfahr sein display: none). Und es kommt nicht auf jQuery an!

Zum jetzigen Zeitpunkt (Version 1.1.1) gibt es die Einschränkungen, dass IE8 nicht unterstützt wird und dass Browserfehler die contenteditablekorrekte Behandlung verhindern .

Nate Sullivan
quelle
2
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();
};
Dustin Poissant
quelle
1

Dies ist mein erster Beitrag zu SO, daher habe ich nicht genug Ruf, um die akzeptierte Antwort zu kommentieren, aber ich musste den Code wie folgt ändern:

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

Der Wechsel von var zu konstant ist unkritisch. Die Hauptänderung ist, dass wir den Selektor loswerden, der tabindex überprüft! = "-1". Wenn das Element später das Attribut tabindex hat UND auf "-1" gesetzt ist, halten wir es NICHT für fokussierbar.

Der Grund, warum ich dies ändern musste, war <input>, dass dieses Element beim Hinzufügen von tabindex = "- 1" zu einem Element immer noch als fokussierbar angesehen wurde, da es mit dem Selektor "input [type = text]: not ([disabled])" übereinstimmt. Meine Änderung entspricht "Wenn wir eine nicht deaktivierte Texteingabe sind und ein tabIndex-Attribut haben und der Wert dieses Attributs -1 ist, sollten wir nicht als fokussierbar betrachtet werden.

Ich glaube, als der Autor der akzeptierten Antwort seine Antwort bearbeitet hat, um das tabIndex-Attribut zu berücksichtigen, hat er dies nicht richtig gemacht. Bitte lassen Sie mich wissen, wenn dies nicht der Fall ist

BrushyAmoeba
quelle
1

Es gibt die tabindex- Eigenschaft, die für die Komponente festgelegt werden kann. Es gibt an, in welcher Reihenfolge die Eingabekomponenten iteriert werden sollen, wenn Sie eine auswählen und die Tabulatortaste drücken. Werte über 0 sind für die benutzerdefinierte Navigation reserviert, 0 ist "in natürlicher Reihenfolge" (würde sich also anders verhalten, wenn es für das erste Element festgelegt wird), -1 bedeutet, dass die Tastatur nicht fokussierbar ist:

<!-- navigate with tab key: -->
<input tabindex="1" type="text"/>
<input tabindex="2" type="text"/>

Es kann auch für etwas anderes als die Texteingabefelder eingestellt werden, aber es ist nicht sehr offensichtlich, was es dort tun würde, wenn überhaupt. Selbst wenn die Navigation funktioniert, ist es vielleicht besser, "natürliche Reihenfolge" für etwas anderes als die sehr offensichtlichen Benutzereingabeelemente zu verwenden.

Nein, Sie benötigen weder JQuery noch Skripte, um diesen benutzerdefinierten Navigationspfad zu unterstützen. Sie können es auf der Serverseite ohne JavaScript-Unterstützung implementieren. Auf der anderen Seite funktioniert die Eigenschaft auch im React-Framework einwandfrei, erfordert sie jedoch nicht.

Audrius Meskauskas
quelle
0

Hier ist eine vollständigere Version der Fokussierung auf das nächste Element. Es folgt den Spezifikationsrichtlinien und sortiert die Liste der Elemente mithilfe von tabindex korrekt. Es wird auch eine umgekehrte Variable definiert, wenn Sie das vorherige Element erhalten möchten.

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]);
}
svarlitskiy
quelle
0

Dies ist eine potenzielle Verbesserung der großartigen Lösung, die @Kano und @Mx angeboten haben. Wenn Sie die TabIndex-Reihenfolge beibehalten möchten, fügen Sie diese Sortierung in der Mitte hinzu:

// 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);
DavB.cs
quelle
0

Sie können dies nennen:

Tab:

$.tabNext();

Umschalt + 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>

Ich ändere jquery.tabbable PlugIn, um abzuschließen.

iHad 169
quelle
Duplikat dieser Antwort , die vom Ersteller dieses jQuery-Plugins veröffentlicht wurde.
mbomb007
0

Nekromantie.
Ich habe eine Menge 0-tabIndexes, die ich über die Tastatur navigieren wollte.
Da in diesem Fall nur die BESTELLUNG der Elemente von Bedeutung war, habe ich es mit verwendetdocument.createTreeWalker

Zuerst erstellen Sie den Filter (Sie möchten nur [sichtbare] Elemente, die ein Attribut "tabIndex" mit einem NUMERICAL-Wert haben.

Dann legen Sie den Wurzelknoten fest, über den hinaus Sie nicht suchen möchten. In meinem Fall this.m_treeist ein ul-Element, das einen toggable Baum enthält. Wenn Sie das gesamte Dokument stattdessen möchten, ersetzen Sie einfach this.m_treemit document.documentElement.

Dann setzen Sie den aktuellen Knoten auf das aktuell aktive Element:

ni.currentNode = el; // el = document.activeElement

Dann kehren Sie zurück ni.nextNode()oder ni.previousNode().

Hinweis:
Dadurch werden die Registerkarten NICHT in der richtigen Reihenfolge zurückgegeben, wenn Sie tabIndices! = 0 haben und die Elementreihenfolge NICHT die tabIndex-Reihenfolge ist. Bei tabIndex = 0 ist tabOrder immer die Elementreihenfolge, weshalb dies (in diesem Fall) funktioniert.

protected createFilter(fn?: (node: Node) => number): NodeFilter
{
    // Accept all currently filtered elements.
    function acceptNode(node: Node): number 
    {
        return NodeFilter.FILTER_ACCEPT;
    }

    if (fn == null)
        fn = acceptNode;


    // Work around Internet Explorer wanting a function instead of an object.
    // IE also *requires* this argument where other browsers don't.
    const safeFilter: NodeFilter = <NodeFilter><any>fn;
    (<any>safeFilter).acceptNode = fn;

    return safeFilter;
}



protected createTabbingFilter(): NodeFilter
{
    // Accept all currently filtered elements.
    function acceptNode(node: Node): number 
    {
        if (!node)
            return NodeFilter.FILTER_REJECT;

        if (node.nodeType !== Node.ELEMENT_NODE)
            return NodeFilter.FILTER_REJECT;

        if (window.getComputedStyle(<Element>node).display === "none")
            return NodeFilter.FILTER_REJECT;

        // "tabIndex": "0"
        if (!(<Element>node).hasAttribute("tabIndex"))
            return NodeFilter.FILTER_SKIP;

        let tabIndex = parseInt((<Element>node).getAttribute("tabIndex"), 10);
        if (!tabIndex || isNaN(tabIndex) || !isFinite(tabIndex))
            return NodeFilter.FILTER_SKIP;

        // if ((<Element>node).tagName !== "LI") return NodeFilter.FILTER_SKIP;

        return NodeFilter.FILTER_ACCEPT;
    }

    return this.createFilter(acceptNode);
}


protected getNextTab(el: HTMLElement): HTMLElement
{
    let currentNode: Node;
    // https://developer.mozilla.org/en-US/docs/Web/API/Document/createNodeIterator
    // https://developer.mozilla.org/en-US/docs/Web/API/Document/createTreeWalker

    // let ni = document.createNodeIterator(el, NodeFilter.SHOW_ELEMENT);
    // let ni = document.createTreeWalker(this.m_tree, NodeFilter.SHOW_ELEMENT);
    let ni = document.createTreeWalker(this.m_tree, NodeFilter.SHOW_ELEMENT, this.createTabbingFilter(), false);

    ni.currentNode = el;

    while (currentNode = ni.nextNode())
    {
        return <HTMLElement>currentNode;
    }

    return el;
}


protected getPreviousTab(el: HTMLElement): HTMLElement
{
    let currentNode: Node;
    let ni = document.createTreeWalker(this.m_tree, NodeFilter.SHOW_ELEMENT, this.createTabbingFilter(), false);
    ni.currentNode = el;

    while (currentNode = ni.previousNode())
    {
        return <HTMLElement>currentNode;
    }

    return el;
}

Beachten Sie, dass die while-Schleife

while (currentNode = ni.nextNode())
{
    // Additional checks here
    // if(condition) return currentNode;
    // else the loop continues;
    return <HTMLElement>currentNode; // everything is already filtered down to what we need here
}

ist nur verfügbar, wenn Sie zusätzliche Kriterien haben, die Sie in dem an createTreeWalker übergebenen Filter nicht filtern können.

Beachten Sie, dass dies TypeScript ist. Sie müssen alle Token hinter Doppelpunkten (:) und zwischen spitzen Klammern (<>) entfernen, z. B. <Element>oder :(node: Node) => numberum gültiges JavaScript zu erhalten.

Hier als Service, die transpilierte JS:

"use strict";
function createFilter(fn) {
    // Accept all currently filtered elements.
    function acceptNode(node) {
        return NodeFilter.FILTER_ACCEPT;
    }
    if (fn == null)
        fn = acceptNode;
    // Work around Internet Explorer wanting a function instead of an object.
    // IE also *requires* this argument where other browsers don't.
    const safeFilter = fn;
    safeFilter.acceptNode = fn;
    return safeFilter;
}
function createTabbingFilter() {
    // Accept all currently filtered elements.
    function acceptNode(node) {
        if (!node)
            return NodeFilter.FILTER_REJECT;
        if (node.nodeType !== Node.ELEMENT_NODE)
            return NodeFilter.FILTER_REJECT;
        if (window.getComputedStyle(node).display === "none")
            return NodeFilter.FILTER_REJECT;
        // "tabIndex": "0"
        if (!node.hasAttribute("tabIndex"))
            return NodeFilter.FILTER_SKIP;
        let tabIndex = parseInt(node.getAttribute("tabIndex"), 10);
        if (!tabIndex || isNaN(tabIndex) || !isFinite(tabIndex))
            return NodeFilter.FILTER_SKIP;
        // if ((<Element>node).tagName !== "LI") return NodeFilter.FILTER_SKIP;
        return NodeFilter.FILTER_ACCEPT;
    }
    return createFilter(acceptNode);
}
function getNextTab(el) {
    let currentNode;
    // https://developer.mozilla.org/en-US/docs/Web/API/Document/createNodeIterator
    // https://developer.mozilla.org/en-US/docs/Web/API/Document/createTreeWalker
    // let ni = document.createNodeIterator(el, NodeFilter.SHOW_ELEMENT);
    // let ni = document.createTreeWalker(this.m_tree, NodeFilter.SHOW_ELEMENT);
    let ni = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_ELEMENT, createTabbingFilter(), false);
    ni.currentNode = el;
    while (currentNode = ni.nextNode()) {
        return currentNode;
    }
    return el;
}
function getPreviousTab(el) {
    let currentNode;
    let ni = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_ELEMENT, createTabbingFilter(), false);
    ni.currentNode = el;
    while (currentNode = ni.previousNode()) {
        return currentNode;
    }
    return el;
}
Stefan Steiger
quelle
-1

Haben Sie für jedes Element, das Sie durchlaufen möchten, Ihre eigenen tabIndex-Werte angegeben? Wenn ja, können Sie Folgendes versuchen:

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
}

Sie verwenden jquery, richtig?

Brian Glaz
quelle
Wir verwenden JQuery nicht, da es die Anwendung beschädigt. : /
JadziaMD
Ok, ich denke ich kann umschreiben ohne jquery, gib mir eine Minute
Brian Glaz
Für jedes Element, an dem wir interessiert sind, sind die Registerkartenindexwerte festgelegt.
JadziaMD
-1

Ich habe die obigen Lösungen überprüft und fand sie ziemlich langwierig. Dies kann mit nur einer Codezeile erreicht werden:

currentElement.nextElementSibling.focus();

oder

currentElement.previousElementSibling.focus();

Hier kann currentElement ein beliebiges Dokument sein, dh document.activeElement oder dies, wenn sich das aktuelle Element im Funktionskontext befindet.

Ich habe Tab- und Shift-Tab-Ereignisse mit dem Keydown-Ereignis verfolgt

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

Sobald Sie die Cursorrichtung haben, können Sie nextElementSibling.focusoder previousElementSibling.focusMethoden verwenden

Manpreet Singh Dhillon
quelle
1
Leider hängt die Reihenfolge der Geschwister nicht mit der Reihenfolge der Registerkarten zusammen, außer durch glücklichen Zufall, und es gibt keine Garantie dafür, dass das vorherige / nächste Geschwister überhaupt fokussierbar ist.
Lawrence Dol