web-dev-qa-db-fra.com

Enter key press se comporte comme un onglet en Javascript

Je cherche à créer un formulaire où appuyer sur la touche Entrée fait passer le focus à l'élément de formulaire "suivant" de la page. La solution que je n'arrête pas de trouver sur le Web est ...

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

Malheureusement, cela ne semble fonctionner que dans IE. Le vrai problème de cette question est donc de savoir si quelqu'un connaît une solution qui fonctionne pour FF et Chrome? De plus, je préférerais ne pas avoir à ajouter les événements onkeydown aux éléments de formulaire eux-mêmes, mais si c'est le seul moyen, il faudra le faire.

Cette question est similaire à question 905222 , mais mérite à elle seule sa propre question.

Edit: aussi, j'ai vu des gens dire que ce n'est pas un bon style, car il diffère du comportement des formulaires auquel les utilisateurs sont habitués. Je suis d'accord! C'est une demande du client :(

62
Ross

J'ai utilisé la logique suggérée par Andrew qui est très efficace. Et voici ma version:

$('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;
    }
});
78
tcdona

Mappez la touche [Entrée] pour qu'elle fonctionne comme la touche [Tab]

J'ai réécrit la réponse de Andre Van Zuydam , qui n'a pas fonctionné pour moi, dans jQuery. Cette caputures à la fois Enter et Shift+EnterEnter onglets en avant, et Shift+Enter onglets en arrière.

J'ai également réécrit la manière dont self est initialisé par l'élément actuellement sélectionné. Le formulaire est également sélectionné de cette façon. Voici le code:

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

La raison textarea

est inclus parce que nous " faisons " voulons y entrer. En outre, une fois dedans, nous ne voulons pas arrêter le comportement par défaut de Enter de mettre dans une nouvelle ligne.

La raison a et button

autoriser l'action par défaut, " et " reste concentré sur l'élément suivant, car ils ne chargent pas toujours une autre page. Il peut y avoir un déclencheur/effet sur ceux-ci tels qu'un accordéon ou un contenu à onglets. Ainsi, une fois que vous avez déclenché le comportement par défaut et que la page produit ses effets spéciaux, vous souhaitez toujours passer à l'élément suivant, car votre déclencheur l'a peut-être bien introduit.

18
6ft Dan

Cela a fonctionné pour moi

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

13
Mayur Sarang

Merci pour le bon script.

Je viens d'ajouter l'événement shift sur la fonction ci-dessus pour revenir entre les éléments, j'ai pensé que quelqu'un pourrait en avoir besoin.

$('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;
}
});
11
Andre Van Zuydam

Il y a des problèmes avec toutes les implémentations données ici. Certains ne fonctionnent pas correctement avec textareas et les boutons submit, la plupart ne vous permettent pas d'utiliser shift pour revenir en arrière, aucun d'entre eux n'utilise de tabindex si vous en avez, et aucun d'entre eux ne passe du dernier au premier Jusqu'au dernier.

Pour que la touche [entrée] agisse comme la touche [tab] tout en fonctionnant correctement avec les zones de texte et les boutons d'envoi, utilisez le code suivant. De plus, ce code vous permet d’utiliser la touche Maj pour revenir en arrière et les onglets se superposent d’avant en arrière.

Code source: 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();
}
6
Mike Bethany

Le plus simple extrait de Vanilla JS que j'ai créé:

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

Fonctionne dans IE 9+ et les navigateurs modernes.

5
Vitaly Kuznetsov

La modification de ce comportement crée en réalité une expérience utilisateur bien meilleure que le comportement par défaut implémenté de manière native. Considérez que le comportement de la touche Entrée est déjà incohérent du point de vue de l'utilisateur, car dans une entrée à une seule ligne, entrée tend à soumettre un formulaire, tandis que dans une zone de texte à plusieurs lignes, elle ajoute simplement une nouvelle ligne au contenu de l'élément. champ.

Je l'ai récemment fait comme ça (utilise 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;
 }
});

Ce n'est pas très efficace, mais fonctionne assez bien et est fiable - ajoutez simplement la classe 'enterastab' à tout élément d'entrée qui devrait se comporter de cette manière.

4
Andrew

J'ai retravaillé la solution OP en une reliure Knockout et j'ai pensé la partager. Merci beaucoup :-)

Voici a Fiddle 

<!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
Damien Sawyer

Voici une directive angular.js pour faire en sorte que vous passiez au champ suivant en vous inspirant des autres réponses. Il y a peut-être un code bizarre ici, car je n'utilise que le jQlite fourni avec angular. Je crois que la plupart des fonctionnalités ici fonctionnent dans tous les navigateurs> 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
    };
});

Voici comment je l'utilise dans l'application sur laquelle je travaille, en ajoutant simplement la directive pdk-next-input-on-enter sur un élément. J'utilise un scanner de codes à barres pour entrer des données dans des champs. La fonction par défaut du scanner est d'émuler un keayboard, en injectant une clé d'entrée après avoir saisi les données du code à barres numérisé.

Ce code a un effet secondaire (positif pour mon cas d'utilisation): s'il déplace le focus sur un bouton, l'événement enter keyup activera l'action du bouton. Cela a très bien fonctionné pour mon flux car le dernier élément de formulaire de mon balisage est un bouton que je veux activer une fois que tous les champs ont été "tabulés" en scannant les codes à barres.

<!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>
2
joshperry

J'ai eu un problème similaire, où je voulais appuyer sur + sur le pavé numérique pour passer au champ suivant. Maintenant, j'ai publié une bibliothèque qui, je pense, vous aidera.

PlusAsTab : Un plugin jQuery pour utiliser le pavé numérique plus comme une touche de tabulation équivalente.

Puisque tu veux enter/ à la place, vous pouvez définir les options. Découvrez quelle clé vous souhaitez utiliser avec le démo jQuery event.which .

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

Vous pouvez l'essayer vous-même dans le PlusAsTab entre en démo .

1
Joel Purra

Si vous le pouviez, je reconsidérerais ceci: l'action par défaut consistant à appuyer sur <Enter> dans un formulaire soumet le formulaire et tout ce que vous faites pour modifier cette action/comportement attendu peut entraîner des problèmes d'utilisation sur le site.

0
Ian Oxley

Vanilla js avec prise en charge des touches Maj + Entrée et possibilité de choisir les balises HTML à mettre au point Devrait fonctionner IE9 +.

  onKeyUp(e) {
    switch (e.keyCode) {
      case 13: //Enter
        var focusableElements = document.querySelectorAll('input, button')
        var index = Array.prototype.indexOf.call(focusableElements, document.activeElement)
        if(e.shiftKey)
          focus(focusableElements, index - 1)
        else
          focus(focusableElements, index + 1)

        e.preventDefault()
        break;
    }
    function focus(elements, index) {
      if(elements[index])
        elements[index].focus()
    }
  }
0
jiv-e

Voici ce que je suis venu avec.

form.addEventListener("submit", (e) => { //On Submit
 let key = e.charCode || e.keyCode || 0 //get the key code
 if (key = 13) { //If enter key
    e.preventDefault()
    const inputs = Array.from(document.querySelectorAll("form input")) //Get array of inputs
    let nextInput = inputs[inputs.indexOf(document.activeElement) + 1] //get index of input after the current input
    nextInput.focus() //focus new input
}
}
0

Essaye ça...

$(document).ready(function () {
    $.fn.enterkeytab = function () {
        $(this).on('keydown', 'input,select,text,button', function (e) {
            var self = $(this)
              , form = self.parents('form:eq(0)')
              , focusable
              , next
            ;
            if (e.keyCode == 13) {
                focusable = form.find('input,a,select').filter(':visible');
                next = focusable.eq(focusable.index(this) + 1);
                if (next.length) {
                    //if disable try get next 10 fields
                    if (next.is(":disabled")){
                        for(i=2;i<10;i++){
                            next = focusable.eq(focusable.index(this) + i);
                            if (!next.is(":disabled"))
                                break;
                        }
                    }
                    next.focus();
                }
                return false;
            }
        });
    }
    $("form").enterkeytab();
});
0
Lucio Pelinson

Je l'ai travailler uniquement en JavaScript. Firefox ne vous laissera pas mettre à jour le code de code source, vous ne pouvez donc que capturer le code de base 13 et le forcer à se concentrer sur l'élément suivant par tabIndex comme si le code clé 9 avait été activé. La partie la plus délicate consiste à trouver le prochain tabIndex. J'ai testé cela uniquement sur IE8-IE10 et Firefox et cela fonctionne:

function ModifyEnterKeyPressAsTab(event)
{
    var caller;
    var key;
    if (window.event)
    {
        caller = window.event.srcElement; //Get the event caller in IE.
        key = window.event.keyCode; //Get the keycode in IE.
    }
    else
    {
        caller = event.target; //Get the event caller in Firefox.
        key = event.which; //Get the keycode in Firefox.
    }
    if (key == 13) //Enter key was pressed.
    {
        cTab = caller.tabIndex; //caller tabIndex.
        maxTab = 0; //highest tabIndex (start at 0 to change)
        minTab = cTab; //lowest tabIndex (this may change, but start at caller)
        allById = document.getElementsByTagName("input"); //Get input elements.
        allByIndex = []; //Storage for elements by index.
        c = 0; //index of the caller in allByIndex (start at 0 to change)
        i = 0; //generic indexer for allByIndex;
        for (id in allById) //Loop through all the input elements by id.
        {
            allByIndex[i] = allById[id]; //Set allByIndex.
            tab = allByIndex[i].tabIndex;
            if (caller == allByIndex[i])
                c = i; //Get the index of the caller.
            if (tab > maxTab)
                maxTab = tab; //Get the highest tabIndex on the page.
            if (tab < minTab && tab >= 0)
                minTab = tab; //Get the lowest positive tabIndex on the page.
            i++;
        }
        //Loop through tab indexes from caller to highest.
        for (tab = cTab; tab <= maxTab; tab++)
        {
            //Look for this tabIndex from the caller to the end of page.
            for (i = c + 1; i < allByIndex.length; i++)
            {
                if (allByIndex[i].tabIndex == tab)
                {
                    allByIndex[i].focus(); //Move to that element and stop.
                    return;
                }
            }
            //Look for the next tabIndex from the start of page to the caller.
            for (i = 0; i < c; i++)
            {
                if (allByIndex[i].tabIndex == tab + 1)
                {
                    allByIndex[i].focus(); //Move to that element and stop.
                    return;
                }
            }
            //Continue searching from the caller for the next tabIndex.
        }

        //The caller was the last element with the highest tabIndex,
        //so find the first element with the lowest tabIndex.
        for (i = 0; i < allByIndex.length; i++)
        {
            if (allByIndex[i].tabIndex == minTab)
            {
                allByIndex[i].focus(); //Move to that element and stop.
                return;
            }
        }
    }
}

Pour utiliser ce code, ajoutez-le à votre balise HTML:

<input id="SomeID" onkeydown="ModifyEnterKeyPressAsTab(event);" ... >

Ou ajoutez-le à un élément en javascript:

document.getElementById("SomeID").onKeyDown = ModifyEnterKeyPressAsTab;

Quelques autres notes:

Je n'avais besoin que de travailler sur mes éléments d'entrée, mais vous pouvez l'étendre à d'autres éléments de document si nécessaire. Pour cela, getElementsByClassName est très utile, mais c'est un tout autre sujet.

Une limitation est qu'il ne fait que des tabulations entre les éléments que vous avez ajoutés à votre tableau allById. Cela n'indique pas que votre navigateur peut contenir d'autres éléments, tels que des barres d'outils et des menus en dehors de votre document html. C'est peut-être une fonctionnalité au lieu d'une limitation. Si vous le souhaitez, interceptez keyCode 9 et ce comportement fonctionnera également avec la touche de tabulation.

0
Jroonk

Beaucoup de réponses ici utilisent e.keyCode et e.which qui sont obsolètes.

Au lieu de cela, vous devriez utiliser e.key === 'Enter'.

Documentation: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

  • Je suis désolé mais je ne peux pas tester ces extraits pour l'instant. Je reviendrai plus tard après l'avoir testé.

Avec HTML:

<body onkeypress="if(event.key==='Enter' && event.target.form){focusNextElement(event); return false;}">

Avec jQuery:

$(window).on('keypress', function (ev)
{
    if (ev.key === "Enter" && ev.currentTarget.form) focusNextElement(ev)
}

Avec Vanilla JS:

document.addEventListener ('keypress', function (ev) {if (ev.key === "Entrée" && ev.currentTarget.form) focusNextElement (ev);});

Vous pouvez prendre la fonction focusNextElement() à partir d’ici: https://stackoverflow.com/a/35173443/3356679

0
oriadam

Vous pouvez utiliser mon code ci-dessous, testé dans Mozilla, IE et Chrome

   // Use to act like tab using enter key
    $.fn.enterkeytab=function(){
         $(this).on('keydown', 'input, select,', function(e) {
        var self = $(this)
          , form = self.parents('form:eq(0)')
          , focusable
          , next
          ;
            if (e.keyCode == 13) {
                focusable = form.find('input,a,select,button').filter(':visible');
                next = focusable.eq(focusable.index(this)+1);
                if (next.length) {
                    next.focus();
                } else {
                    alert("wd");
                    //form.submit();
                }
                return false;
            }
        });

    }

Comment utiliser?

$ ("# forme"). enterkeytab (); // entrer l'onglet clé

0
Francis Tudlong