Fokuskan Elemen Berikutnya Di Indeks Tab

103

Saya mencoba untuk memindahkan fokus ke elemen berikutnya dalam urutan tab berdasarkan elemen saat ini yang memiliki fokus. Sejauh ini saya belum menemukan apa pun dalam pencarian saya.

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

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

Tentu saja nextElementByTabIndex adalah bagian penting agar ini berfungsi. Bagaimana cara menemukan elemen berikutnya dalam urutan tab? Solusinya harus berdasarkan menggunakan JScript dan bukan sesuatu seperti JQuery.

JadziaMD
sumber
3
mengapa Anda memiliki baris ini currentElementId = "";?
1
Saya rasa tidak ada browser yang mengekspos informasi urutan tab - dan algoritme yang digunakan oleh browser itu sendiri terlalu rumit untuk ditiru. Mungkin Anda dapat membatasi kebutuhan Anda, misalnya "hanya mempertimbangkan input, buttondan memberi textareatag dan mengabaikan tabindexatribut".
Wladimir Palant
Kami perlu melihat .newElementByTabIndexkode Anda karena itulah yang tidak berfungsi.
0x499602D2
2
Kemudian lagi, mungkin pembatasan untuk tag tertentu tidak diperlukan - seseorang dapat memeriksa apakah focus()metode tersebut ada.
Wladimir Palant
1
@David Itu fungsi yang tidak ada, oleh karena itu pertanyaan saya. : D
JadziaMD

Jawaban:

24

Tanpa jquery: Pertama-tama, pada elemen tab -able Anda, tambahkan class="tabable"ini akan memungkinkan kami memilihnya nanti. (Jangan lupa awalan pemilih kelas "." Pada kode di bawah)

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
sumber
16
Solusi tanpa harus menambahkan nama ke setiap elemen (karena ada banyak cara untuk membuatnya jika memungkinkan) akan ideal.
JadziaMD
3
oke, apakah ini untuk formulir? Jika semua elemen yang Anda inginkan adalah elemen input, Anda dapat mengganti baris var tabbables = document.getElementsByName("tabable");dengan var tabbables = document.getElementsByTagName("input");sebaliknya
Brian Glaz
var tabbables = document.querySelectorAll("input, textarea, button")// IE8 +, dapatkan referensi ke semua tabbable tanpa memodifikasi HTML Anda.
Greg
2
class = "tabbable" daripada menggunakan atribut name
Chris F Carroll
4
Perhatikan bahwa menggunakan flexbox urutan elemen di DOM berbeda dengan yang terlihat di browser. Hanya memilih elemen tabbable berikutnya tidak akan berfungsi saat Anda mengubah urutan elemen menggunakan flexbox.
Haneev
75

Saya tidak pernah menerapkan ini, tetapi saya telah melihat masalah yang serupa, dan inilah yang akan saya coba.

Coba ini dulu

Pertama, saya akan melihat apakah Anda bisa dengan mudah mengaktifkan keypressperistiwa untuk tombol Tab pada elemen yang saat ini memiliki fokus. Mungkin ada cara berbeda untuk melakukan ini untuk browser yang berbeda.

Jika itu tidak berhasil, Anda harus bekerja lebih keras…

Mereferensikan implementasi jQuery, Anda harus:

  1. Dengarkan Tab dan Shift + Tab
  2. Ketahui elemen mana yang dapat ditab
  3. Pahami cara kerja urutan tab

1. Dengarkan Tab dan Shift + Tab

Mendengarkan Tab dan Shift + Tab mungkin tercakup dengan baik di tempat lain di web, jadi saya akan melewatkan bagian itu.

2. Ketahui elemen mana yang dapat ditab

Mengetahui elemen mana yang dapat ditab lebih rumit. Pada dasarnya, elemen dapat ditab jika dapat difokuskan dan tidak memiliki tabindex="-1"set atribut . Jadi kita harus bertanya elemen mana yang bisa difokuskan. Elemen berikut dapat difokuskan:

  • input, select, textarea, button, Dan objectunsur-unsur yang tidak cacat.
  • adan areaelemen yang memiliki hrefatau memiliki nilai numerik untuk tabindexset.
  • elemen apa pun yang memiliki nilai numerik untuk tabindexset.

Selain itu, sebuah elemen hanya dapat difokuskan jika:

  • Tidak ada nenek moyangnya display: none.
  • Nilai yang dihitung dari visibilityadalah visible. Ini berarti bahwa leluhur terdekat yang harus visibilityditetapkan harus memiliki nilai visible. Jika tidak ada leluhur yang visibilitymenetapkan, maka nilai yang dihitung adalah visible.

Detail selengkapnya ada di jawaban Stack Overflow lainnya .

3. Pahami cara kerja tab order

Urutan tab elemen dalam dokumen dikontrol oleh tabindexatribut. Jika tidak ada nilai yang ditetapkan, maka tabindexefektif 0.

The tabindexAgar dokumen tersebut adalah: 1, 2, 3, ..., 0.

Awalnya, ketika bodyelemen (atau tidak ada elemen) memiliki fokus, elemen pertama dalam urutan tab adalah bukan nol terendah tabindex. Jika beberapa elemen memiliki yang sama tabindex, Anda kemudian pergi dalam urutan dokumen sampai Anda mencapai elemen terakhir dengan itu tabindex. Kemudian Anda pindah ke level terendah berikutnya tabindexdan proses berlanjut. Terakhir, selesaikan elemen tersebut dengan nol (atau kosong) tabindex.

Chris Calo
sumber
37

Inilah sesuatu yang saya buat untuk tujuan ini:

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

Fitur:

  • kumpulan elemen yang dapat difokuskan yang dapat dikonfigurasi
  • tidak perlu jQuery
  • berfungsi di semua browser modern
  • cepat & ringan
Mx.
sumber
2
Ini adalah solusi paling efektif dan ramah sumber daya. Terima kasih! Ini adalah skrip kerja lengkap saya: stackoverflow.com/a/40686327/1589669
eapo
Saya menambahkan potongan di bawah ini untuk memasukkan pengurutan berdasarkan TabIndex eksplisit focussable.sort (sort_by_TabIndex)
DavB.cs
1
Terbaik ! Ini harus menjadi yang kompleks: yang nextElementSiblingmungkin tidak focusable, yang focusable berikutnya mungkin tidak saudara kandung.
Tinmarino
Pendekatan yang baik, tetapi harus memungkinkan untuk setiap masukan yang bukan dari jenis hiddendan juga mencakup textareadan select.
Lucero
23

Saya membuat plugin jQuery sederhana yang melakukan hal ini. Ini menggunakan pemilih ': tabbable' dari jQuery UI untuk menemukan elemen 'tabbable' berikutnya dan memilihnya.

Contoh penggunaan:

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

Inti dari jawabannya terletak pada menemukan elemen selanjutnya:

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

Pemakaian:

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

Perhatikan bahwa saya tidak peduli dengan prioritas tabIndex.

André Werlang
sumber
3
Bagaimana jika urutan tabindex bertentangan dengan urutan dokumen? Saya pikir array harus diurutkan berdasarkan nomor tabindex kemudian berdasarkan urutan dokumen
Chris F Carroll
Ya, itu akan lebih "sesuai spesifikasi". Saya tidak yakin tentang kasus tepi, tentang elemen induk, dll.
André Werlang
Bagaimana jika item yang bukan salah satu tag tersebut memiliki atribut tabindex?
Matt Pennington
1
@AttPenningnning Ini akan diabaikan. Filter adalah (upaya) untuk mempercepat pencarian, silakan beradaptasi.
André Werlang
3

Seperti yang disebutkan dalam komentar di atas, menurut saya tidak ada browser yang mengekspos informasi pesanan tab. Berikut adalah perkiraan yang disederhanakan tentang apa yang dilakukan browser untuk mendapatkan elemen berikutnya dalam urutan tab:

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

Ini hanya mempertimbangkan beberapa tag dan mengabaikan tabindexatribut tetapi mungkin cukup tergantung pada apa yang Anda coba capai.

Wladimir Palant
sumber
3

Tampaknya Anda dapat memeriksa tabIndexproperti elemen untuk menentukan apakah dapat difokuskan. Elemen yang tidak dapat difokuskan memiliki tabindex"-1".

Maka Anda hanya perlu mengetahui aturan untuk tab stop:

  • tabIndex="1" memiliki prioritas tertinggi.
  • tabIndex="2" memiliki prioritas tertinggi berikutnya.
  • tabIndex="3" berikutnya, dan seterusnya.
  • tabIndex="0" (atau tabbable secara default) memiliki prioritas terendah.
  • tabIndex="-1" (atau tidak tabbable secara default) tidak bertindak sebagai tab stop.
  • Untuk dua elemen yang memiliki tabIndex yang sama, yang muncul pertama kali di DOM memiliki prioritas lebih tinggi.

Berikut adalah contoh bagaimana membuat daftar tab stop, secara berurutan, menggunakan Javascript murni:

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

Kami pertama kali menjalankan DOM, mengumpulkan semua tab stop secara berurutan dengan indeksnya. Kami kemudian menyusun daftar terakhir. Perhatikan bahwa kita menambahkan item dengan tabIndex="0"di akhir daftar, setelah item dengan a tabIndex1, 2, 3, dll.

Untuk contoh yang berfungsi penuh, di mana Anda dapat melihat-lihat menggunakan tombol "enter", lihat biola ini .

chowey
sumber
2

Tabbable adalah paket JS kecil yang memberikan daftar semua elemen tabbable agar tab . Jadi Anda bisa menemukan elemen Anda di dalam daftar itu, lalu fokus pada entri daftar berikutnya.

Paket dengan benar menangani kasus edge rumit yang disebutkan dalam jawaban lain (misalnya, tidak ada leluhur yang dapat display: none). Dan itu tidak tergantung pada jQuery!

Pada tulisan ini (versi 1.1.1), ada peringatan bahwa ia tidak mendukung IE8, dan bug browser mencegahnya menangani contenteditabledengan benar.

Nate Sullivan
sumber
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
sumber
1

Ini adalah posting pertama saya di SO, jadi saya tidak memiliki reputasi yang cukup untuk mengomentari jawaban yang diterima, tetapi saya harus mengubah kodenya menjadi yang berikut:

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

Perubahan var menjadi konstan tidak kritis. Perubahan utama adalah kita menyingkirkan selektor yang memeriksa tabindex! = "-1". Kemudian, jika elemen memiliki atribut tabindex DAN disetel ke "-1", kami TIDAK menganggapnya dapat difokuskan.

Alasan saya perlu mengubahnya adalah karena saat menambahkan tabindex = "- 1" ke sebuah <input>, elemen ini masih dianggap dapat difokuskan karena cocok dengan pemilih "input [type = text]: not ([disabled])". Perubahan saya setara dengan "jika kita adalah input teks yang tidak dinonaktifkan, dan kita memiliki atribut tabIndex, dan nilai atribut itu -1, maka kita tidak boleh dianggap dapat difokuskan.

Saya percaya bahwa ketika penulis jawaban yang diterima mengedit jawaban mereka untuk memperhitungkan atribut tabIndex, mereka tidak melakukannya dengan benar. Tolong beri tahu saya jika bukan ini masalahnya

BrushyAmoeba
sumber
1

Ada properti tabindex yang dapat disetel pada komponen. Ini menentukan urutan komponen input harus diulang ketika memilih satu dan menekan tab. Nilai di atas 0 dicadangkan untuk navigasi khusus, 0 adalah "dalam urutan alami" (jadi akan berperilaku berbeda jika disetel untuk elemen pertama), -1 berarti tidak dapat difokuskan pada keyboard:

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

Ini juga dapat diatur untuk hal lain selain bidang input teks tetapi tidak terlalu jelas apa yang akan dilakukannya di sana, jika ada. Bahkan jika navigasi berfungsi, mungkin lebih baik menggunakan "urutan alami" untuk hal lain selain elemen masukan pengguna yang sangat jelas.

Tidak, Anda tidak memerlukan JQuery atau skrip apa pun untuk mendukung jalur navigasi kustom ini. Anda dapat menerapkannya di sisi server tanpa dukungan JavaScript. Dari sisi lain, properti ini juga berfungsi dengan baik dalam kerangka kerja React tetapi tidak membutuhkannya.

Audrius Meskauskas
sumber
0

Berikut adalah versi yang lebih lengkap dari pemfokusan pada elemen berikutnya. Ini mengikuti pedoman spesifikasi dan mengurutkan daftar elemen dengan benar menggunakan tabindex. Juga variabel terbalik didefinisikan jika Anda ingin mendapatkan elemen sebelumnya.

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.dll
sumber
0

Ini adalah perangkat tambahan potensi untuk solusi besar yang @Kano dan @Mx ditawarkan. Jika Anda ingin mempertahankan pengurutan TabIndex, tambahkan urutan ini di tengah:

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

Anda bisa menyebutnya:

Tab:

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

Saya memodifikasi PlugIn jquery.tabbable hingga selesai.

iHad 169
sumber
Duplikat dari jawaban ini , yang telah diposting oleh pembuat plugin jQuery itu.
mbomb007
0

Necromancing.
Saya memiliki 0-tabIndexes, yang ingin saya navigasikan dengan keyboard.
Karena dalam kasus itu, hanya ORDER elemen yang penting, saya melakukannya dengan menggunakandocument.createTreeWalker

Jadi pertama-tama Anda membuat filter (Anda hanya menginginkan elemen [terlihat], yang memiliki atribut "tabIndex" dengan nilai NUMERIK.

Kemudian Anda mengatur simpul akar, yang di luar itu Anda tidak ingin mencari. Dalam kasus saya, this.m_treeadalah ul-elemen yang mengandung pohon toggable. Jika Anda menginginkan seluruh dokumen, cukup ganti this.m_treedengan document.documentElement.

Kemudian Anda mengatur node saat ini ke elemen aktif saat ini:

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

Kemudian Anda kembali ni.nextNode()atau ni.previousNode().

Catatan:
ini TIDAK akan mengembalikan tab dalam urutan yang benar jika Anda memiliki tabIndices! = 0 dan urutan elemen BUKAN urutan tabIndex. Dalam kasus tabIndex = 0, tabOrder selalu merupakan urutan elemen, itulah mengapa ini berfungsi (dalam kasus itu).

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

Perhatikan bahwa while-loop

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
}

hanya ada jika Anda ingin jika Anda memiliki kriteria tambahan yang tidak dapat Anda filter di filter yang diteruskan ke createTreeWalker.

Perhatikan bahwa ini adalah TypeScript, Anda perlu menghapus semua token di belakang titik dua (:), dan di antara tanda kurung sudut (<>), misalnya, <Element>atau :(node: Node) => numberuntuk mendapatkan JavaScript yang valid.

Di sini sebagai layanan, JS yang ditranslasikan:

"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
sumber
-1

Apakah Anda menentukan nilai tabIndex Anda sendiri untuk setiap elemen yang ingin Anda telusuri? jika demikian, Anda dapat mencoba ini:

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
}

Anda menggunakan jquery, bukan?

Brian Glaz
sumber
Kami tidak menggunakan JQuery karena merusak aplikasi. : /
JadziaMD
Oke, saya rasa saya bisa menulis ulang tanpa menggunakan jquery, beri saya waktu sebentar
Brian Glaz
Setiap elemen yang kami minati memiliki nilai indeks tab yang ditetapkan.
JadziaMD
-1

Saya memeriksa solusi di atas dan menemukan mereka cukup panjang. Itu dapat dicapai hanya dengan satu baris kode:

currentElement.nextElementSibling.focus();

atau

currentElement.previousElementSibling.focus();

di sini currentElement dapat berupa document.activeElement apa pun atau ini jika elemen saat ini ada dalam konteks fungsi.

Saya melacak acara tab dan shift-tab dengan acara 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 == '';
    }
});

setelah Anda memiliki arah kursor maka Anda dapat menggunakan nextElementSibling.focusatau previousElementSibling.focusmetode

Manpreet Singh Dhillon
sumber
1
Sayangnya urutan saudara kandung tidak terkait dengan urutan tab, kecuali kebetulan yang menyenangkan, dan tidak ada jaminan saudara kandung yang sebelumnya / berikutnya bahkan akan dapat difokuskan.
Lawrence Dol