Фокус Следующий элемент Вкладка Индекс

Я пытаюсь переместить фокус на следующий элемент в последовательности вкладок, основанный на текущем элементе, который имеет фокус. До сих пор я ничего не обнаружил в своих поисках.

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

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

Конечно, nextElementByTabIndex - ключевая часть для этого. Как найти следующий элемент в последовательности вкладок? Решение должно основываться на JScript, а не на JQuery.

Ответ 1

Без jquery: Прежде всего, на ваших элементах с вкладками добавьте class="tabable" это позволит нам выбрать их позже. (Не забудьте префикс селектора классов "." В коде ниже)

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

Ответ 2

Я никогда не реализовывал это, но я искал аналогичную проблему, и вот что я хотел бы попробовать.

Попробуйте сначала

Во-первых, я бы посмотрел, можно ли просто запустить событие keypress для клавиши Tab в элементе, который в настоящее время имеет фокус. Для разных браузеров может быть другой способ.

Если это не сработает, вам придется больше работать...

Ссылаясь на реализацию jQuery, вы должны:

  • Прослушать вкладку и Shift + Tab
  • Знайте, какие элементы доступны для табуляции
  • Поймите, как работает вкладка

1. Слушайте вкладку и Shift + Tab

Прослушивание Tab и Shift + Tab, вероятно, хорошо освещено в других местах в Интернете, поэтому я пропущу эту часть.

2. Знайте, какие элементы доступны для табуляции

Зная, какие элементы являются табуляционными, сложнее. В принципе, элемент является tab-able, если он настраивается и не имеет атрибута tabindex="-1". Итак, мы должны спросить, какие элементы являются ориентируемыми. Следующие элементы могут быть сфокусированы:

  • input, select, textarea, button и object элементы, которые не отключены.
  • a и area, которые имеют href или имеют числовое значение для tabindex.
  • любой элемент, который имеет числовое значение для tabindex.

Кроме того, элемент фокусируется только в том случае, если:

  • Ни один из его предков display: none.
  • Вычисленное значение visibility равно visible. Это означает, что ближайший предок для установки visibility должен иметь значение visible. Если ни один из предков не имеет visibility, тогда вычисленное значение равно visible.

Более подробная информация находится в другом ответе.

3. Поймите, как работает вкладка

Порядок вкладок элементов в документе контролируется атрибутом tabindex. Если значение не установлено, tabindex эффективно 0.

Заказ tabindex для документа: 1, 2, 3,..., 0.

Изначально, когда элемент body (или без элемента) имеет фокус, первый элемент в порядке табуляции является самым низким ненулевым tabindex. Если несколько элементов имеют одинаковый tabindex, вы переходите в порядок документа, пока не достигнете последнего элемента с помощью tabindex. Затем вы переходите к следующему низшему tabindex, и процесс продолжается. Наконец, закончите с теми элементами с нулевым (или пустым) tabindex.

Ответ 3

Я создал простой плагин jQuery, который делает именно это. Он использует селектор ": tabbable" jQuery UI, чтобы найти следующий элемент "tabbable" и выбирает его.

Пример использования:

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

Ответ 4

Здесь я что-то создаю для этой цели:

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

Особенности:

  • настраиваемый набор фокусируемых элементов
  • не нужно jQuery
  • работает во всех современных браузерах
  • быстрый и легкий

Ответ 5

Ядро ответа заключается в нахождении следующего элемента:

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

Использование:

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

Обратите внимание: мне не важно определить приоритеты tabIndex.

Ответ 6

Как уже упоминалось выше, я не думаю, что любые браузеры отображают информацию о заказе. Здесь упрощенное приближение того, что браузер делает для получения следующего элемента в порядке вкладок:

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

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

Это относится только к некоторым тегам и игнорирует атрибут tabindex, но может быть достаточно в зависимости от того, чего вы пытаетесь достичь.

Ответ 7

Кажется, что вы можете проверить свойство tabIndex элемента, чтобы определить, является ли оно настраиваемым. Элемент, который не фокусируется, имеет tabIndex от -1.

Тогда вам просто нужно знать правила для табуляторов:

  • tabIndex="1" имеет наивысший приоритет.
  • tabIndex="2" имеет следующий высокий приоритет.
  • tabIndex="3" следующий и т.д.
  • tabIndex="0" (или tabbable по умолчанию) имеет самый низкий приоритет.
  • tabIndex="-1" (или не tabbable по умолчанию) не действует как остановка табуляции.
  • Для двух элементов, имеющих один и тот же tabIndex, тот, который появляется первым в DOM, имеет более высокий приоритет.

Вот пример того, как построить список табуляторов в последовательности, используя чистый Javascript:

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

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

getTabStops(o, a, document.body);

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

Сначала мы начинаем DOM, собирая все вкладки в последовательности с их индексом. Затем мы собираем окончательный список. Обратите внимание, что мы добавляем элементы с tabIndex="0" в самом конце списка, после элементов с tabIndex из 1, 2, 3 и т.д.

Для полнофункционального примера, где вы можете вставить вкладку с помощью клавиши "enter", ознакомьтесь с этим fiddle.

Ответ 8

Вы указали свои собственные значения tabIndex для каждого элемента, который вы хотите перебрать? если это так, вы можете попробовать следующее:

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

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

Вы используете jquery, правильно?

Ответ 9

Надеюсь, что это будет полезно.

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

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

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

то используйте простой javascript

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

Ответ 10

Tabbable - это небольшой пакет JS, который предоставляет вам список всех элементов с вкладками в порядке вкладок, Таким образом, вы можете найти свой элемент в этом списке, а затем сосредоточиться на записи следующего списка.

Пакет правильно обрабатывает сложные случаи кросс, упомянутые в других ответах (например, ни один из предков не может быть display: none). И это не зависит от jQuery!

Начиная с этой записи (версия 1.1.1), у нее есть предостережения, что она не поддерживает IE8, и что ошибки браузера не позволяют правильной обработке contenteditable.

Ответ 11

Это мой первый пост на SO, поэтому у меня недостаточно репутации, чтобы прокомментировать принятый ответ, но мне пришлось изменить код на следующее:

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

Изменение переменной var на константу является некритическим. Главное изменение заключается в том, что мы избавляемся от селектора, который проверяет tabindex!= "-1". Затем, если элемент имеет атрибут tabindex И он установлен в "-1", мы НЕ рассматриваем его сфокусированным.

Причина, по которой мне нужно было изменить это, заключалась в том, что при добавлении tabindex = "- 1" в <input> этот элемент по-прежнему считался сфокусированным, поскольку он соответствует "input [type = text]: not ([disabled])". Мое изменение эквивалентно "если мы являемся неотключенным текстовым вводом, и у нас есть атрибут tabIndex, а значение этого атрибута равно -1, то мы не должны считаться сфокусированными.

Я считаю, что, когда автор принятого ответа отредактировал свой ответ на аккаунт для атрибута tabIndex, они не сделали этого правильно. Пожалуйста, дайте мне знать, если это не так.

Ответ 12

Вот более полная версия фокусировки на следующем элементе. Он следует спецификациям спецификаций и правильно сортирует список элементов, используя tabindex. Также обратная переменная определяется, если вы хотите получить предыдущий элемент.

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

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

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

Ответ 13

Я проверил вышеупомянутые решения и нашел их довольно длинными. Это можно сделать всего одной строкой кода:

currentElement.nextElementSibling.focus();

или же

currentElement.previousElementSibling.focus();

здесь currentElement может быть любым, т.е. document.activeElement или this, если текущий элемент находится в контексте функции.

Я отслеживал вкладки и Shift-Tab с событиями 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 == '';
        }
    });

если у вас есть направление курсора, вы можете использовать методы nextElementSibling.focus или previousElementSibling.focus