Нажатие клавиши ввода ведет себя как вкладка в Javascript - PullRequest
64 голосов
/ 18 июня 2009

Я ищу создание формы, в которой нажатие клавиши ввода заставляет фокус перейти к «следующему» элементу формы на странице. Решение, которое я продолжаю находить в Интернете, это ...

 <body onkeydown="if(event.keyCode==13){event.keyCode=9; return event.keyCode}">

К сожалению, это работает только в IE. Так что настоящий вопрос в этом вопросе, если кто-нибудь знает решение, которое работает для FF и Chrome? Кроме того, я бы предпочел не добавлять события onkeydown к самим элементам формы, но если это единственный способ, он должен это сделать.

Эта проблема похожа на вопрос 905222 , но, на мой взгляд, заслуживает отдельного вопроса.

Редактировать: также я видел, как люди поднимают вопрос о том, что это не очень хороший стиль, поскольку он отличается от поведения форм, к которому привыкли пользователи. Согласен! Это запрос клиента: (

Ответы [ 21 ]

78 голосов
/ 26 августа 2012

Я использовал предложенную Эндрю логику, которая очень эффективна. И это моя версия:

$('body').on('keydown', 'input, select, textarea', function(e) {
    var self = $(this)
      , form = self.parents('form:eq(0)')
      , focusable
      , next
      ;
    if (e.keyCode == 13) {
        focusable = form.find('input,a,select,button,textarea').filter(':visible');
        next = focusable.eq(focusable.index(this)+1);
        if (next.length) {
            next.focus();
        } else {
            form.submit();
        }
        return false;
    }
});
18 голосов
/ 18 декабря 2014

Карта [Enter], чтобы работать как клавиша [Tab]

Я переписал Andre Van Zuydam ответ, который мне не помог, в jQuery. Это фиксирует Enter и Shift + Enter . Введите вкладки вперед и Shift + Введите вкладки назад.

Я также переписал способ инициализации self текущим объектом в фокусе. Форма также выбрана таким образом. Вот код:

// Map [Enter] key to work like the [Tab] key
// Daniel P. Clark 2014

// Catch the keydown for the entire document
$(document).keydown(function(e) {

  // Set self as the current item in focus
  var self = $(':focus'),
      // Set the form by the current item in focus
      form = self.parents('form:eq(0)'),
      focusable;

  // Array of Indexable/Tab-able items
  focusable = form.find('input,a,select,button,textarea,div[contenteditable=true]').filter(':visible');

  function enterKey(){
    if (e.which === 13 && !self.is('textarea,div[contenteditable=true]')) { // [Enter] key

      // If not a regular hyperlink/button/textarea
      if ($.inArray(self, focusable) && (!self.is('a,button'))){
        // Then prevent the default [Enter] key behaviour from submitting the form
        e.preventDefault();
      } // Otherwise follow the link/button as by design, or put new line in textarea

      // Focus on the next item (either previous or next depending on shift)
      focusable.eq(focusable.index(self) + (e.shiftKey ? -1 : 1)).focus();

      return false;
    }
  }
  // We need to capture the [Shift] key and check the [Enter] key either way.
  if (e.shiftKey) { enterKey() } else { enterKey() }
});

Причина textarea

включено потому, что мы " do " хотим включить его. Кроме того, однажды мы не хотим останавливать поведение по умолчанию Enter от ввода новой строки.

Причина a и button

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

13 голосов
/ 20 октября 2016

Это сработало для меня

 $(document).on('keydown', ':tabbable', function (e) {

 if (e.which == 13  || e.keyCode == 13  ) 
 {      e.preventDefault();
        var $canfocus = $(':tabbable:visible')
        var index = $canfocus.index(document.activeElement) + 1;
        if (index >= $canfocus.length) index = 0;
        $canfocus.eq(index).focus();
}   

});

JsFiddle

11 голосов
/ 20 ноября 2013

Спасибо за хороший сценарий.

Я только что добавил событие shift для вышеуказанной функции, чтобы вернуться между элементами, я думал, что кому-то это может понадобиться.

$('body').on('keydown', 'input, select, textarea', function(e) {
var self = $(this)
  , form = self.parents('form:eq(0)')
  , focusable
  , next
  , prev
  ;

if (e.shiftKey) {
 if (e.keyCode == 13) {
     focusable =   form.find('input,a,select,button,textarea').filter(':visible');
     prev = focusable.eq(focusable.index(this)-1); 

     if (prev.length) {
        prev.focus();
     } else {
        form.submit();
    }
  }
}
  else
if (e.keyCode == 13) {
    focusable = form.find('input,a,select,button,textarea').filter(':visible');
    next = focusable.eq(focusable.index(this)+1);
    if (next.length) {
        next.focus();
    } else {
        form.submit();
    }
    return false;
}
});
6 голосов
/ 30 декабря 2016

Самый простой ванильный фрагмент JS, который я придумал:

document.addEventListener('keydown', function (event) {
  if (event.keyCode === 13 && event.target.nodeName === 'INPUT') {
    var form = event.target.form;
    var index = Array.prototype.indexOf.call(form, event.target);
    form.elements[index + 1].focus();
    event.preventDefault();
  }
});

Работает в IE 9+ и современных браузерах.

6 голосов
/ 12 ноября 2014

Есть проблемы со всеми реализациями, приведенными здесь. Некоторые не работают должным образом с текстовыми областями и кнопками отправки, большинство не позволяют использовать сдвиг для перехода назад, никто из них не использует табличные индексы, если они у вас есть, и ни один из них не переходит с последнего на первый или первый до последнего.

Чтобы клавиша [вводить] действовала аналогично клавише [вкладка], но при этом все равно работала с текстовыми областями и кнопки отправки использовали следующий код. Кроме того, этот код позволяет использовать клавишу Shift для перехода назад, а табуляция оборачивается спереди назад и сзади вперед.

Исходный код: https://github.com/mikbe/SaneEnterKey

CoffeeScript

mbsd_sane_enter_key = ->
  input_types = "input, select, button, textarea"
  $("body").on "keydown", input_types, (e) ->
    enter_key = 13
    tab_key = 9

    if e.keyCode in [tab_key, enter_key]
      self = $(this)

      # some controls should just press enter when pressing enter
      if e.keyCode == enter_key and (self.prop('type') in ["submit", "textarea"])
        return true

      form = self.parents('form:eq(0)')

      # Sort by tab indexes if they exist
      tab_index = parseInt(self.attr('tabindex'))
      if tab_index
        input_array = form.find("[tabindex]").filter(':visible').sort((a,b) -> 
          parseInt($(a).attr('tabindex')) - parseInt($(b).attr('tabindex'))
        )
      else
        input_array = form.find(input_types).filter(':visible')

      # reverse the direction if using shift
      move_direction = if e.shiftKey then -1 else 1
      new_index = input_array.index(this) + move_direction

      # wrap around the controls
      if new_index == input_array.length
        new_index = 0
      else if new_index == -1
        new_index = input_array.length - 1

      move_to = input_array.eq(new_index)
      move_to.focus()
      move_to.select()

      false

$(window).on 'ready page:load', ->
  mbsd_sane_enter_key()

JavaScript

var mbsd_sane_enter_key = function() {
  var input_types;
  input_types = "input, select, button, textarea";

  return $("body").on("keydown", input_types, function(e) {
    var enter_key, form, input_array, move_direction, move_to, new_index, self, tab_index, tab_key;
    enter_key = 13;
    tab_key = 9;

    if (e.keyCode === tab_key || e.keyCode === enter_key) {
      self = $(this);

      // some controls should react as designed when pressing enter
      if (e.keyCode === enter_key && (self.prop('type') === "submit" || self.prop('type') === "textarea")) {
        return true;
      }

      form = self.parents('form:eq(0)');

      // Sort by tab indexes if they exist
      tab_index = parseInt(self.attr('tabindex'));
      if (tab_index) {
        input_array = form.find("[tabindex]").filter(':visible').sort(function(a, b) {
          return parseInt($(a).attr('tabindex')) - parseInt($(b).attr('tabindex'));
        });
      } else {
        input_array = form.find(input_types).filter(':visible');
      }

      // reverse the direction if using shift
      move_direction = e.shiftKey ? -1 : 1;
      new_index = input_array.index(this) + move_direction;

      // wrap around the controls
      if (new_index === input_array.length) {
        new_index = 0;
      } else if (new_index === -1) {
        new_index = input_array.length - 1;
      }

      move_to = input_array.eq(new_index);
      move_to.focus();
      move_to.select();
      return false;
    }
  });
};

$(window).on('ready page:load', function() {
  mbsd_sane_enter_key();
}
4 голосов
/ 12 апреля 2013

Я переделал решение OPs в привязку Knockout и решил поделиться им. Большое спасибо: -)

Вот Скрипка

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" type="text/javascript"></script>
    <script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js" type="text/javascript"></script>


</head>
<body>

    <div data-bind="nextFieldOnEnter:true">
        <input type="text" />
        <input type="text" />
        <select>
          <option value="volvo">Volvo</option>
          <option value="saab">Saab</option>
          <option value="mercedes">Mercedes</option>
          <option value="audi">Audi</option>
        </select>
        <input type="text" />
        <input type="text" />
    </div>


    <script type="text/javascript">
    ko.bindingHandlers.nextFieldOnEnter = {
        init: function(element, valueAccessor, allBindingsAccessor) {
            $(element).on('keydown', 'input, select', function (e) {
                var self = $(this)
                , form = $(element)
                  , focusable
                  , next
                ;
                if (e.keyCode == 13) {
                    focusable = form.find('input,a,select,button,textarea').filter(':visible');
                    var nextIndex = focusable.index(this) == focusable.length -1 ? 0 : focusable.index(this) + 1;
                    next = focusable.eq(nextIndex);
                    next.focus();
                    return false;
                }
            });
        }
    };

    ko.applyBindings({});
    </script>
</body>
</html>
4 голосов
/ 04 октября 2010

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

Я недавно сделал это так (использует jQuery):

$('input.enterastab, select.enterastab, textarea.enterastab').live('keydown', function(e) {
 if (e.keyCode==13) {
  var focusable = $('input,a,select,button,textarea').filter(':visible');
  focusable.eq(focusable.index(this)+1).focus();
  return false;
 }
});

Это не очень эффективно, но работает достаточно хорошо и надежно - просто добавьте класс 'enterastab' к любому элементу ввода, который должен вести себя таким образом.

2 голосов
/ 22 января 2014

Вот директива angular.js, которая заставляет команду enter перейти к следующему полю, используя другие ответы в качестве вдохновения. Здесь есть некоторый, возможно, странно выглядящий код, потому что я использую только jQlite, упакованный с angular. Я считаю, что большинство функций здесь работают во всех браузерах> IE8.

angular.module('myapp', [])
.directive('pdkNextInputOnEnter', function() {
    var includeTags = ['INPUT', 'SELECT'];

    function link(scope, element, attrs) {
        element.on('keydown', function (e) {
            // Go to next form element on enter and only for included tags
            if (e.keyCode == 13 && includeTags.indexOf(e.target.tagName) != -1) {
                // Find all form elements that can receive focus
                var focusable = element[0].querySelectorAll('input,select,button,textarea');

                // Get the index of the currently focused element
                var currentIndex = Array.prototype.indexOf.call(focusable, e.target)

                // Find the next items in the list
                var nextIndex = currentIndex == focusable.length - 1 ? 0 : currentIndex + 1;

                // Focus the next element
                if(nextIndex >= 0 && nextIndex < focusable.length)
                    focusable[nextIndex].focus();

                return false;
            }
        });
    }

    return {
        restrict: 'A',
        link: link
    };
});

Вот как я использую его в приложении, над которым я работаю, просто добавив директиву pdk-next-input-on-enter для элемента. Я использую сканер штрих-кода для ввода данных в поля, функция сканера по умолчанию - эмуляция клавиатуры, ввод ключа ввода после ввода данных отсканированного штрих-кода.

У этого кода есть один побочный эффект (положительный для моего сценария использования), если он перемещает фокус на кнопку, событие enter keyup приведет к активации действия кнопки. Это сработало очень хорошо для моего потока, так как последний элемент формы в моей разметке - это кнопка, которую я хочу активировать после того, как все поля будут «вставлены» с помощью сканирования штрих-кодов.

<!DOCTYPE html>
<html ng-app=myapp>
  <head>
      <script src="angular.min.js"></script>
      <script src="controller.js"></script>
  </head>
  <body ng-controller="LabelPrintingController">
      <div class='.container' pdk-next-input-on-enter>
          <select ng-options="p for p in partNumbers" ng-model="selectedPart" ng-change="selectedPartChanged()"></select>
          <h2>{{labelDocument.SerialNumber}}</h2>
          <div ng-show="labelDocument.ComponentSerials">
              <b>Component Serials</b>
              <ul>
                  <li ng-repeat="serial in labelDocument.ComponentSerials">
                      {{serial.name}}<br/>
                      <input type="text" ng-model="serial.value" />
                  </li>
              </ul>
          </div>
          <button ng-click="printLabel()">Print</button>
      </div>
  </body>
</html>
1 голос
/ 17 февраля 2012

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

PlusAsTab : плагин jQuery для использования клавиши numpad plus в качестве эквивалента клавиши табуляции.

Поскольку вы хотите , введите / & crarr; , вместо этого вы можете установить параметры. Узнайте, какой ключ вы хотите использовать с событием jQuery. Какое демо .

JoelPurra.PlusAsTab.setOptions({
  // Use enter instead of plus
  // Number 13 found through demo at
  // https://api.jquery.com/event.which/
  key: 13
});

// Matches all inputs with name "a[]" (needs some character escaping)
$('input[name=a\\[\\]]').plusAsTab();

Вы можете попробовать сами в PlusAsTab, введите как вкладку демо .

...