Ustaw fokus na następny element w indeksie zakładek


103

Próbuję przenieść fokus do następnego elementu w sekwencji kart na podstawie bieżącego elementu, który ma fokus. Jak dotąd nie znalazłem niczego w moich poszukiwaniach.

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

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

Oczywiście nextElementByTabIndex jest kluczową częścią, aby to zadziałało. Jak znaleźć następny element w sekwencji tabulatorów? Rozwiązanie musiałoby być oparte na języku JScript, a nie na czymś takim jak JQuery.


3
dlaczego masz tę linię currentElementId = "";?

1
Nie sądzę, aby jakakolwiek przeglądarka ujawniała informacje o kolejności tabulatorów - a algorytm używany przez same przeglądarki jest zbyt skomplikowany, aby go powielić. Może można ograniczyć swoje wymagania, na przykład „tylko rozważyć input, buttonoraz textareatagi i ignorować tabindexatrybut”.
Wladimir Palant

Musimy zobaczyć Twój .newElementByTabIndexkod, ponieważ to nie działa.
0x499602D2

2
Z drugiej strony, może ograniczenie do poszczególnych tagów jest zbędne - można sprawdzić, czy focus()metoda istnieje.
Wladimir Palant

1
@David To funkcja, która nie istnieje, dlatego moje pytanie. : D
JadziaMD Sierpnia

Odpowiedzi:


24

Bez jquery: Po pierwsze, dodaj class="tabable"to do elementów z zakładkami, co pozwoli nam wybrać je później. (Nie zapomnij o przedrostku selektora klasy „.” W poniższym kodzie)

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

16
Idealne byłoby rozwiązanie bez konieczności dodawania nazwy do każdego elementu (ponieważ jest wiele do zrobienia, jeśli jest to wykonalne).
JadziaMD

3
ok, czy to dla formularza? Jeśli wszystkie elementy są elementami, które mają wejść można zamienić linię var tabbables = document.getElementsByName("tabable");ze var tabbables = document.getElementsByTagName("input");zamiast
Brian Glaz

var tabbables = document.querySelectorAll("input, textarea, button")// IE8 +, uzyskaj odniesienie do wszystkich tabel bez modyfikowania kodu HTML.
Greg,

2
class = "tabbable" zamiast używać atrybutu name
Chris F Carroll

4
Zauważ, że używając flexboksa kolejność elementów w DOM jest inna niż wizualnie w przeglądarce. Samo wybranie następnego elementu tabelarycznego nie działa, gdy zmieniasz kolejność elementów za pomocą flexbox.
Haneev

75

Nigdy tego nie zaimplementowałem, ale przyjrzałem się podobnemu problemowi i oto, co chciałbym spróbować.

Spróbuj najpierw tego

Po pierwsze, chciałbym sprawdzić, czy możesz po prostu uruchomić keypresszdarzenie dla klawisza Tab na elemencie, który jest aktualnie aktywny. W różnych przeglądarkach może istnieć inny sposób zrobienia tego.

Jeśli to nie zadziała, będziesz musiał pracować ciężej…

Odwołując się do implementacji jQuery, musisz:

  1. Posłuchaj Tab i Shift + Tab
  2. Dowiedz się, które elementy można umieszczać na kartach
  3. Dowiedz się, jak działa kolejność zakładek

1. Nasłuchuj Tab i Shift + Tab

Słuchanie Tab i Shift + Tab są prawdopodobnie dobrze opisane w innym miejscu w Internecie, więc pominę tę część.

2. Dowiedz się, które elementy są umieszczane w tabulatorach

Wiedza, które elementy są umieszczane na kartach, jest trudniejsza. Zasadniczo element może obsługiwać tabulator, jeśli można go tabindex="-1"ustawić na fokus i nie ma ustawionego atrybutu . Musimy więc zapytać, które elementy można skupić. Można skupić się na następujących elementach:

  • input, select, textarea, button, A objectelementy, które nie są wyłączone.
  • ai areaelementy, które mają hreflub mają wartość liczbową dla tabindexzestawu.
  • dowolny element, który ma wartość liczbową tabindexzestawu.

Ponadto element można ustawić jako fokus tylko wtedy, gdy:

  • Żaden z jej przodków nie jest display: none.
  • Obliczona wartość visibilityto visible. Oznacza to, że najbliższy przodek, który ma zostać visibilityustawiony, musi mieć wartość visible. Jeśli żaden przodek nie został visibilityustawiony, obliczona wartość to visible.

Więcej szczegółów znajduje się w innej odpowiedzi na temat przepełnienia stosu .

3. Dowiedz się, jak działa kolejność zakładek

Kolejność tabulacji elementów w dokumencie jest kontrolowana przez tabindexatrybut. Jeśli nie ustawiono żadnej wartości, tabindexefektywnie jest 0.

tabindexZamówienie na dokumencie: 1, 2, 3, ..., 0.

Początkowo, gdy bodyaktywny jest element (lub żaden element), pierwszy element w kolejności tabulacji jest najniższy niezerowy tabindex. Jeśli wiele elementów ma to samo tabindex, przejdź w kolejności dokumentu, aż dotrzesz do ostatniego elementu z tym tabindex. Następnie przechodzisz do następnej najniższej tabindexi proces trwa. Na koniec zakończ z tymi elementami z zerem (lub pustym) tabindex.


37

Oto coś, co zbudowałem w tym celu:

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

Cechy:

  • konfigurowalny zestaw elementów do ogniskowania
  • nie jest potrzebne jQuery
  • działa we wszystkich nowoczesnych przeglądarkach
  • szybki i lekki

2
To najbardziej efektywne i oszczędzające zasoby rozwiązanie. Dziękuję Ci! Oto mój pełny działający skrypt: stackoverflow.com/a/40686327/1589669
eapo

Dodałem poniżej fragment kodu, aby uwzględnić sortowanie według jawnego TabIndex focussable.sort (sort_by_TabIndex)
DavB.cs

1
Najlepsze! To musi być tak złożone: nextElementSiblingmoże nie być zogniskowany, następny może nie być rodzeństwem.
Tinmarino

Dobre podejście, ale powinno pozwolić na wszelkie dane wejściowe, które nie są typowe, hiddena także obejmować textareai select.
Lucero

23

Stworzyłem prostą wtyczkę jQuery, która właśnie to robi. Używa selektora „: tabbable” w interfejsie użytkownika jQuery, aby znaleźć następny element „tabbable” i wybrać go.

Przykładowe użycie:

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

8

Sedno odpowiedzi polega na znalezieniu kolejnego elementu:

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

Stosowanie:

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

Zauważ, że nie obchodzi mnie ustalanie priorytetów tabIndex.


3
Co się stanie, jeśli kolejność tabindex będzie sprzeczna z kolejnością dokumentów? Myślę, że tablicę należy posortować według numeru tabindex, a następnie według kolejności dokumentów
Chris F Carroll

Tak, to byłoby bardziej „zgodne ze specyfikacją”. Nie jestem pewien co do przypadków
skrajnych

Co się stanie, jeśli element, który nie jest jednym z tych tagów, ma atrybut tabindex?
Matt Pennington

1
@MattPennington Zostanie zignorowane. Filtr jest (próbą) przyspieszenia wyszukiwania, nie krępuj się.
André Werlang

3

Jak wspomniano w powyższym komentarzu, nie sądzę, aby jakakolwiek przeglądarka ujawniała informacje o kolejności zakładek. Tutaj uproszczone przybliżenie tego, co robi przeglądarka, aby pobrać następny element w kolejności tabulacji:

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

Uwzględnia to tylko niektóre tagi i ignoruje tabindexatrybut, ale może wystarczyć w zależności od tego, co próbujesz osiągnąć.


3

Wygląda na to, że można sprawdzić tabIndexwłaściwość elementu, aby określić, czy można go ustawić na fokus. Element, na którym nie można ustawić fokusu, ma wartość tabindex„-1”.

Następnie musisz tylko znać zasady dotyczące tabulatorów:

  • tabIndex="1" ma najwyższy priorytet.
  • tabIndex="2" ma następny najwyższy priorytet.
  • tabIndex="3" jest następny i tak dalej.
  • tabIndex="0" (lub domyślnie tabelaryczny) ma najniższy priorytet.
  • tabIndex="-1" (lub domyślnie bez tabulacji) nie działa jako tabulator.
  • W przypadku dwóch elementów, które mają ten sam tabIndex, ten, który pojawia się jako pierwszy w DOM, ma wyższy priorytet.

Oto przykład, jak zbudować listę tabulatorów, w kolejności, używając czystego 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]);
}

Najpierw przechodzimy po DOM, zbierając wszystkie tabulatory w kolejności z ich indeksem. Następnie tworzymy ostateczną listę. Zauważ, że dodajemy pozycje z tabIndex="0"na samym końcu listy, po pozycjach z tabIndex1, 2, 3 itd.

Aby uzyskać w pełni działający przykład, w którym możesz tabulować za pomocą klawisza „enter”, sprawdź to skrzypce .


2

Tabbable to mały pakiet JS, który zawiera listę wszystkich elementów tabelarycznych w kolejności tabulacji . Możesz więc znaleźć swój element na tej liście, a następnie skupić się na następnym wpisie listy.

Pakiet poprawnie obsługuje skomplikowane przypadki skrajne wymienione w innych odpowiedziach (np. Żaden przodek nie może być display: none). I to nie zależy od jQuery!

W chwili pisania tego tekstu (wersja 1.1.1) ma zastrzeżenia, że ​​nie obsługuje IE8, a błędy przeglądarki uniemożliwiają jej obsługę contenteditable poprawną .


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

1

To jest mój pierwszy post na SO, więc nie mam wystarczającej reputacji, aby skomentować zaakceptowaną odpowiedź, ale musiałem zmodyfikować kod na następujący:

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

Zmiana zmiennej na stałą nie jest krytyczna. Główna zmiana polega na tym, że pozbywamy się selektora sprawdzającego tabindex! = "-1". Później, jeśli element ma atrybut tabindex ORAZ jest ustawiony na „-1”, NIE uważamy go za zogniskowany.

Powodem, dla którego musiałem to zmienić, było to, że podczas dodawania tabindex = "- 1" do elementu <input>, ten element był nadal uważany za focussable, ponieważ pasuje do selektora "input [type = text]: not ([disabled])". Moja zmiana jest równoważna z „jeśli nie wyłączamy funkcji wprowadzania tekstu i mamy atrybut tabIndex, a wartość tego atrybutu wynosi -1, wówczas nie powinniśmy być uważani za focussable.

Uważam, że gdy autor zaakceptowanej odpowiedzi zredagował swoją odpowiedź tak, aby uwzględnić atrybut tabIndex, nie zrobił tego poprawnie. Daj mi znać, jeśli tak nie jest


1

Istnieje właściwość tabindex, którą można ustawić w komponencie. Określa, w jakiej kolejności komponenty wejściowe powinny być iterowane po wybraniu jednego z nich i naciśnięciu klawisza Tab. Wartości powyżej 0 są zarezerwowane dla niestandardowej nawigacji, 0 jest „w kolejności naturalnej” (więc zachowywałoby się inaczej, gdyby zostało ustawione dla pierwszego elementu), -1 oznacza, że ​​nie można fokusować za pomocą klawiatury:

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

Można go również ustawić na coś innego niż pola wprowadzania tekstu, ale nie jest bardzo oczywiste, co by tam zrobiło, jeśli w ogóle cokolwiek. Nawet jeśli nawigacja działa, może lepiej jest użyć „naturalnego porządku” do czegokolwiek innego niż bardzo oczywiste elementy wprowadzane przez użytkownika.

Nie, nie potrzebujesz JQuery ani żadnych skryptów do obsługi tej niestandardowej ścieżki nawigacji. Możesz go zaimplementować po stronie serwera bez obsługi JavaScript. Z drugiej strony właściwość działa również dobrze w środowisku React, ale nie wymaga tego.


0

Oto bardziej kompletna wersja skupienia się na kolejnym elemencie. Jest zgodny z wytycznymi specyfikacji i poprawnie sortuje listę elementów przy użyciu tabindex. Definiuje się również zmienną odwrotną, jeśli chcesz uzyskać poprzedni element.

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

To potencjalne ulepszenie wspaniałego rozwiązania, które oferowały @Kano i @Mx . Jeśli chcesz zachować kolejność TabIndex, dodaj to sortowanie w środku:

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

Możesz to nazwać:

Patka:

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

Zmodyfikowałem wtyczkę jquery.tabbable, aby zakończyć.


Duplikat tej odpowiedzi , który został opublikowany przez twórcę tej wtyczki jQuery.
mbomb007

0

Nekromancja.
Mam dużo 0-tabIndexów, po których chciałem nawigować za pomocą klawiatury.
Ponieważ w tym przypadku liczyła się tylko KOLEJNOŚĆ elementów, zrobiłem to używającdocument.createTreeWalker

Więc najpierw utwórz filtr (potrzebujesz tylko [widocznych] elementów, które mają atrybut „tabIndex” z wartością NUMERYCZNĄ.

Następnie ustawiasz węzeł główny, poza którym nie chcesz szukać. W moim przypadku this.m_treejest to element ul zawierający przełączalne drzewo. Jeśli chcesz cały dokument zamiast, po prostu zastąpić this.m_treez document.documentElement.

Następnie ustawiasz bieżący węzeł na bieżący aktywny element:

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

Następnie wrócisz ni.nextNode()lub ni.previousNode().

Uwaga:
to NIE zwróci tabulatorów we właściwej kolejności, jeśli masz tabIndices! = 0, a kolejność elementów NIE jest kolejnością tabIndex. W przypadku tabIndex = 0, tabOrder jest zawsze kolejnością elementów, dlatego to działa (w tym przypadku).

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

Zwróć uwagę, że pętla while

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
}

jest tam tylko wtedy, gdy chcesz, jeśli masz dodatkowe kryteria, których nie możesz filtrować w filtrze przekazanym do createTreeWalker.

Zauważ, że to jest TypeScript, musisz usunąć wszystkie tokeny znajdujące się za dwukropkami (:) i między nawiasami ostrymi (<>), np. <Element>Lub:(node: Node) => number aby uzyskać poprawny JavaScript.

Tutaj jako usługa transpiled 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;
}

-1

Czy określiłeś własne wartości tabIndex dla każdego elementu, przez który chcesz przechodzić? jeśli tak, możesz spróbować tego:

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
}

Używasz jquery, prawda?


Nie używamy JQuery, ponieważ psuje aplikację. : /
JadziaMD

Ok, myślę, że mogę przepisać bez użycia jquery, daj mi chwilę
Brian Glaz

Każdy interesujący nas element ma ustawione wartości indeksu tabulacji.
JadziaMD

-1

Sprawdziłem powyższe rozwiązania i znalazłem je dość obszerne. Można to zrobić za pomocą tylko jednej linii kodu:

currentElement.nextElementSibling.focus();

lub

currentElement.previousElementSibling.focus();

tutaj currentElement może być dowolnym, np. document.activeElement lub to, jeśli bieżący element jest w kontekście funkcji.

Śledziłem zdarzenia dotyczące zakładek i shift-tab za pomocą zdarzenia keydown

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

gdy masz już kierunek kursora, możesz użyć metod nextElementSibling.focuslubpreviousElementSibling.focus


1
Niestety kolejność rodzeństwa nie jest związana z kolejnością tabulacji, chyba że przez szczęśliwy zbieg okoliczności i nie ma gwarancji, że poprzedni / następny brat będzie w ogóle możliwy do skupienia.
Lawrence Dol,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.