Comment le bouton d'envoi par défaut d'un formulaire HTML est-il déterminé?

210

Si un formulaire est soumis mais pas par un bouton spécifique, tel que

  • en appuyant Enter
  • utilisation HTMLFormElement.submit()dans JS

comment un navigateur est-il censé déterminer lequel des multiples boutons d'envoi, le cas échéant, utiliser comme celui sur lequel vous appuyez?

Ceci est significatif à deux niveaux:

  • appeler un onclickgestionnaire d'événements attaché à un bouton d'envoi
  • les données renvoyées au serveur web

Mes expériences jusqu'à présent ont montré que:

  • en appuyant sur Enter, Firefox, Opera et Safari utilisent le premier bouton d'envoi du formulaire
  • en appuyant sur Enter, IE utilise le premier bouton d'envoi ou aucun en fonction des conditions que je n'ai pas pu comprendre
  • tous ces navigateurs n'en utilisent aucun pour une soumission JS

Que dit la norme?

Si cela peut aider, voici mon code de test (le PHP ne concerne que ma méthode de test, pas ma question elle-même)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Test</title>
</head>

<body>

<h1>Get</h1>
<dl>
<?php foreach ($_GET as $k => $v) echo "<dt>$k</dt><dd>$v</dd>"; ?>
</dl>

<h1>Post</h1>
<dl>
<?php foreach ($_POST as $k => $v) echo "<dt>$k</dt><dd>$v</dd>"; ?>
</dl>

<form name="theForm" method="<?php echo isset($_GET['method']) ? $_GET['method'] : 'get'; ?>" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>">
    <input type="text" name="method" />
    <input type="submit" name="action" value="Button 1" onclick="alert('Button 1'); return true" />
    <input type="text" name="stuff" />
    <input type="submit" name="action" value="Button 2" onclick="alert('Button 2'); return true" />
    <input type="button" value="submit" onclick="document.theForm.submit();" />
</form>

</body></html>
Stewart
la source

Réponses:

115

Si vous soumettez le formulaire via Javascript (c'est-à formElement.submit()- dire ou quelque chose d'équivalent), aucun des boutons d'envoi n'est considéré comme réussi et aucune de leurs valeurs n'est incluse dans les données soumises. (Notez que si vous soumettez le formulaire en utilisant submitElement.click()alors la soumission à laquelle vous avez fait référence est considérée comme active; cela ne relève pas vraiment de la compétence de votre question car ici le bouton de soumission est sans ambiguïté mais je pensais que je l'inclurais pour les personnes qui lisent la première partie et se demandent comment réussir un bouton d'envoi via la soumission de formulaire JS. Bien sûr, les gestionnaires onsubmit du formulaire se déclencheront toujours de cette façon alors qu'ils ne le feraient pas viaform.submit() c'est donc une autre marmite de poisson ...)

Si le formulaire est soumis en appuyant sur Entrée dans un champ non textarea, alors c'est à l'agent utilisateur de décider ce qu'il veut ici. Les spécifications ne disent rien sur la soumission d'un formulaire à l'aide de la touche Entrée dans un champ de saisie de texte (si vous cliquez sur un bouton et l'activez en utilisant l'espace ou autre, il n'y a aucun problème car ce bouton de soumission spécifique est utilisé sans ambiguïté). Tout ce qu'il dit, c'est qu'un formulaire doit être soumis lorsqu'un bouton d'envoi est activé, ce n'est même pas une exigence que le fait d'appuyer sur, par exemple, une entrée de texte soumettra le formulaire.

Je crois qu'Internet Explorer choisit le bouton d'envoi qui apparaît en premier dans la source; J'ai le sentiment que Firefox et Opera choisissent le bouton avec le tabindex le plus bas, retombant sur le premier défini si rien d'autre n'est défini. Il y a aussi quelques complications quant à savoir si les soumissions ont un attribut de valeur non par défaut IIRC.

Le point à retenir est qu'il n'y a pas de norme définie pour ce qui se passe ici et que c'est entièrement au gré du navigateur - dans la mesure du possible, dans tout ce que vous faites, essayez d'éviter de vous fier à un comportement particulier. Si vous devez vraiment le savoir, vous pouvez probablement découvrir le comportement des différentes versions du navigateur, mais quand j'ai étudié cela il y a quelque temps, il y avait des conditions assez compliquées (qui bien sûr sont susceptibles de changer avec les nouvelles versions du navigateur) et je conseillerais à éviter si possible!

Andrzej Doyle
la source
1
Concernant le para 2: Ma question fondamentale était de savoir s'il y avait quoi que ce soit dans une spécification à l'effet de "Si le navigateur fournit un moyen de soumettre un formulaire sans spécifier un contrôle de soumission ...". Mais je comprends de la réponse de David qu'il n'y en a pas. Mon point sur IE était que, parfois, appuyer sur Entrée soumet un formulaire et ne définit aucun bouton de soumission. Une conséquence est que, si vous avez plusieurs formulaires à soumettre au même script, vous ne pouvez pas compter sur les boutons de soumission pour les distinguer. Cela est apparu dans le projet sur lequel je travaille.
Stewart
1
Section de support de premier paragraphe très utile - merci.
rbassett du
63

HTML 4 ne le rend pas explicite. Le brouillon de travail HTML5 actuel spécifie que le premier bouton d'envoi doit être la valeur par défaut :

formLe bouton par défaut d' un élément est le premier bouton de soumission dans l'ordre de l'arborescence dont le propriétaire du formulaire est cet formélément.

Si l'agent utilisateur prend en charge le fait de laisser l'utilisateur soumettre un formulaire implicitement (par exemple, sur certaines plates-formes, appuyer sur la touche "Entrée" alors qu'un champ de texte est focalisé soumet implicitement le formulaire), le faire pour un formulaire dont le bouton par défaut a une activation définie Le comportement doit obliger l'agent utilisateur à exécuter des étapes d'activation de clic synthétique sur ce bouton par défaut .

Quentin
la source
Ok, c'est ce que c'est censé faire. Puis-je utiliser?
Pacerier
Je suis heureux de voir la "soumission implicite" dans HTML5.
Clint Pachl
61

Andrezj a à peu près tout compris ... mais voici une solution multi-navigateur simple.

Prenez une forme comme celle-ci:

<form>
    <input/>
    <button type="submit" value="some non-default action"/>
    <button type="submit" value="another non-default action"/>
    <button type="submit" value="yet another non-default action"/>

    <button type="submit" value="default action"/>
</form>

et refactoriser ceci:

<form>
    <input/>

    <button style="overflow: visible !important; height: 0 !important; width: 0 !important; margin: 0 !important; border: 0 !important; padding: 0 !important; display: block !important;" type="submit" value="default action"/>

    <button type="submit" value="some non-default action"/>
    <button type="submit" value="another non-default action"/>
    <button type="submit" value="yet another non-default action"/>
    <button type="submit" value="still another non-default action"/>

    <button type="submit" value="default action"/>
</form>

Étant donné que la spécification W3C indique que plusieurs boutons de soumission sont valides, mais omet des conseils sur la façon dont l'agent utilisateur doit les gérer, les fabricants de navigateurs sont laissés à implémenter comme ils l'entendent. J'ai constaté qu'ils soumettront le premier bouton d'envoi du formulaire ou soumettront le bouton d'envoi suivant après le champ du formulaire qui a actuellement le focus.

Malheureusement, il suffit d'ajouter un style de affichage: aucun; ne fonctionnera pas car la spécification W3C indique que tout élément caché doit être exclu des interactions utilisateur. Alors cachez-le à la place!

Ci-dessus, un exemple de la solution que j'ai fini par mettre en production. Si vous appuyez sur la touche Entrée, la soumission par défaut du formulaire se comporte comme prévu, même lorsque d'autres valeurs non par défaut sont présentes et précèdent le bouton de soumission par défaut dans le DOM. Bonus pour l'interaction souris / clavier avec des entrées utilisateur explicites tout en évitant les gestionnaires javascript.

Remarque: la tabulation dans le formulaire n'affichera le focus pour aucun élément visuel, mais entraînera toujours la sélection du bouton invisible. Pour éviter ce problème, définissez simplement les attributs tabindex en conséquence et omettez un attribut tabindex sur le bouton d'envoi invisible. Bien qu'il puisse sembler déplacé de promouvoir ces styles en! Important, ils devraient empêcher tout cadre ou style de bouton existant d'interférer avec ce correctif. En outre, ces styles en ligne sont définitivement de mauvaise forme, mais nous prouvons des concepts ici ... pas en écrivant du code de production.

James
la source
5
Merci. Je suis tombé sur l' écran: pas de problème non plus. Au lieu de minimiser le bouton, vous pouvez également le déplacer hors de la page en le entourant d'un <div style="position: fixed; left: -1000px; top: -1000px />. Encore une chose à se soucier: le simple fait de réduire ou d'éloigner le bouton du flux de page avec CSS peut entraîner des problèmes WAI, car les lecteurs d'écran verront toujours le bouton par défaut.
Stefan Haberl
2
MERCI @James pour cette réponse détaillée.
Nathan
1
cela a fonctionné pour moi, merci. J'ai dû ajouter ceci pour la bordure du bouton: aucun; pour le faire disparaître complètement
Macumbaomuerte
4
Vous pouvez simplement définir l'attribut tabindex du bouton caché sur -1
tvanc
2
Pourquoi ça <input/>?
Sz.
16

Je pense que ce message aiderait si quelqu'un veut le faire avec jQuery:

http://greatwebguy.com/programming/dom/default-html-button-submit-on-enter-with-jquery/

La solution de base est:

$(function() {
    $("form input").keypress(function (e) {
    if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
        $('input[type=submit].default').click();
        return false;
    } else {
        return true;
    }
    });
});

et un autre que j'ai aimé était:

jQuery(document).ready(function() {
$("form input, form select").live('keypress', function (e) {
if ($(this).parents('form').find('button[type=submit].default, input[type=submit].default').length <= 0)
return true;

if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
$(this).parents('form').find('button[type=submit].default, input[type=submit].default').click();
return false;
} else {
return true;
}
});
}); 
Rodrigo Caballero
la source
jQuery.Event a toujours son .wichattribut. Pas besoin non plus de se rabattre sur les DOM .keyCode(ou .charCoded'ailleurs).
Arjan
Un autre problème concernant jQuery ici est la double requête inutile. Puisqu'il n'est pas nécessaire de retourner true, en outre, la version jQuery devient vraiment claire et courte sur l'intégration des réductions conseillées par Arjan auparavant.
soletan
J'ai utilisé keydown au lieu de keypress (éviter d'attendre keyup) et j'ai ajouté e.preventDefault();au début de cette fonction pour éviter l'exécution du "vrai" bouton par défaut. Fonctionne très bien.
Matt Roy
6

J'avais un formulaire avec 11 boutons d'envoi et il utilisait toujours le premier bouton d'envoi lorsque l'utilisateur appuyait sur Entrée. J'ai lu ailleurs que ce n'est pas une bonne idée (mauvaise pratique) d'avoir plus d'un bouton d'envoi sur un formulaire, et la meilleure façon de le faire est d'avoir le bouton que vous voulez par défaut, comme seul bouton d'envoi sur le formulaire. Les autres boutons doivent être transformés en "TYPE = BUTTON" et un événement onClick ajouté qui appelle votre propre routine d'envoi en Javascript. Quelque chose comme ça :-

<SCRIPT Language="JavaScript">
function validform()
{
  // do whatever you need to validate the form, and return true or false accordingly
}

function mjsubmit()
{
  if (validform()) { document.form1.submit(); return true;}
  return false;
}
</SCRIPT>
<INPUT TYPE=BUTTON NAME="button1" VALUE="button1" onClick="document.form1.submitvalue='button1'; return mjsubmit();">
<INPUT TYPE=BUTTON NAME="button2" VALUE="button2" onClick="document.form1.submitvalue='button2'; return mjsubmit();">
<INPUT TYPE=SUBMIT NAME="button3" VALUE="button3" onClick="document.form1.submitvalue='button3'; return validform();">
<INPUT TYPE=BUTTON NAME="button4" VALUE="button4" onClick="document.form1.submitvalue='button4'; return mjsubmit();">

Ici, button3 est la valeur par défaut, et bien que vous soumettiez le formulaire par programme avec les autres boutons, la routine mjsubmit les valide. HTH.

graphique
la source
19
Mauvaise idée. Votre formulaire ne fonctionnera pas avec JS désactivé. JS doit être utilisé discrètement.
BalusC
7
Très peu de navigateurs ont JS désactivé par défaut, même ceux des iPod! Sur le formulaire que je citais, JS off le rendrait inutile! Tant de choses dépendent du fonctionnement de JS.
graphique
7
Désactivé par défaut ou non, ceux qui ont désactivé JS ne devraient pas avoir à l'activer juste pour contourner un petit problème idiot comme celui-ci.
Stewart
2
quel type de formulaire a 11 boutons d'envoi ?? ( curiosité )
Ben
2
La plus grande raison pour laquelle je vois la validation de Javascript discret est que le Javascript intrusif a tendance à être compliqué. Je ne sais pas comment qualifier ce que je pense, mais quand je vois des pages absolument dépendantes de JS, je parie aussi que je peux faire des trucs délicats avec mon DOM et faire quelque chose de involontaire sur le serveur.
Samantha Branham
6

Cela peut maintenant être résolu en utilisant flexbox:

HTML

<form>
    <h1>My Form</h1>
    <label for="text">Input:</label>
    <input type="text" name="text" id="text"/>

    <!-- Put the elements in reverse order -->
    <div class="form-actions">
        <button>Ok</button> <!-- our default action is first -->
        <button>Cancel</button>
    </div>
</form>

CSS

.form-actions {
    display: flex;
    flex-direction: row-reverse; /* reverse the elements inside */
}

Explaination
utilisant la boîte flex, nous pouvons inverser l'ordre des éléments dans un conteneur qui utilise le display: flexen utilisant également la règle CSS: flex-direction: row-reverse. Cela ne nécessite ni CSS ni éléments cachés. Pour les navigateurs plus anciens qui ne prennent pas en charge Flexbox, ils obtiennent toujours une solution viable mais les éléments ne seront pas inversés.

Démo
http://codepen.io/Godwin/pen/rLVKjm

Godwin
la source
2

J'ai eu du mal avec la même question car j'avais un bouton de soumission au milieu de la redirection vers une autre page, comme ceci:

<button type="submit" onclick="this.form.action = '#another_page'">More</button>

Lorsque l'utilisateur a appuyé sur la touche Entrée, ce bouton a été cliqué au lieu d'un autre bouton de soumission.

J'ai donc fait quelques tests primitifs en créant un à partir de plusieurs boutons d'envoi et différentes options de visibilité et un événement onclick alertant sur quel bouton on a cliqué: https://jsfiddle.net/aqfy51om/1/

Navigateurs et OS que j'ai utilisés pour les tests:

LES FENÊTRES

  • Google Chrome 43 (allez sur google: D)
  • Mozilla Firefox 38
  • Internet Explorer 11
  • Opera 30.0

OSX

  • Google Chrome 43
  • Safari 7.1.6

La plupart de ces navigateurs ont cliqué sur le tout premier bouton malgré les options de visibilité appliquées sauf IE et Safari qui ont cliqué sur le troisième bouton, qui est "visible" à l'intérieur du conteneur "caché":

<div style="width: 0; height: 0; overflow: hidden;">
    <button type="submit" class="btn btn-default" onclick="alert('Hidden submit button #3 was clicked');">Hidden submit button #3</button>
</div>

Donc, ma suggestion, que je vais utiliser moi-même, est la suivante:

Si votre formulaire a plusieurs boutons d'envoi avec une signification différente, alors incluez le bouton d'envoi avec l'action par défaut au début du formulaire qui est soit:

  1. Entièrement visible
  2. Enveloppé dans un récipient avec style="width: 0; height: 0; overflow: hidden;"

EDIT Une autre option pourrait être de décaler le bouton (toujours au début de la) style="position: absolute; left: -9999px; top: -9999px;", je l'ai juste essayé dans IE - travaillé, mais je n'ai aucune idée de quoi d'autre il peut bousiller, par exemple l'impression ..

Kristian
la source
1

De vos commentaires:

Une conséquence est que, si vous avez plusieurs formulaires à soumettre au même script, vous ne pouvez pas compter sur les boutons de soumission pour les distinguer.

Je laisse tomber un <input type="hidden" value="form_name" /> dans chaque formulaire.

Si vous soumettez avec javascript: ajoutez des événements d'envoi aux formulaires, pas cliquez sur les événements à leurs boutons. Les utilisateurs de Saavy ne touchent pas souvent leur souris.

Ryan Florence
la source
En effet, mais cela soulève cette question: stackoverflow.com/questions/541869/…
Stewart
1

Lorsque vous avez plusieurs boutons d'envoi dans un seul formulaire et qu'un utilisateur appuie sur la touche ENTRÉE pour soumettre le formulaire à partir d'un champ de texte, ce code remplace la fonctionnalité par défaut, en appelant l'événement d'envoi sur le formulaire à partir de l'événement de pression de touche. Voici ce code:

$('form input').keypress(function(e){

    if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)){ $(e.target).closest('form').submit(); return false; }
    else return true;

});
axiom82
la source
Simple et résout le problème
toddmo
1

Étrange que le premier bouton Enter passe toujours au premier bouton, qu'il soit visible ou non, par exemple en utilisant jquery show/hide(). L'ajout d'un attribut .attr('disabled', 'disabled')empêche la réception complète du bouton Soumettre. C'est un problème par exemple lors du réglage de la visibilité du bouton Insérer / Modifier + Supprimer dans les boîtes de dialogue d'enregistrement. J'ai trouvé moins hackeux et simple en plaçant Edit devant Insert

<button type="submit" name="action" value="update">Update</button>
<button type="submit" name="action" value="insert">Insert</button>
<button type="submit" name="action" value="delete">Delete</button>

et utilisez le code javascript:

$("#formId button[type='submit'][name='action'][value!='insert']").hide().attr('disabled', 'disabled');
$("#formId button[type='submit'][name='action'][value='insert']").show().removeAttr('disabled');
TMa
la source
0

ma recette:

<form>
<input type=hidden name=action value=login><!-- the magic! -->

<input type=text name=email>
<input type=text name=password>

<input type=submit name=action value=login>
<input type=submit name=action value="forgot password">
</form>

Il enverra le champ caché par défaut si aucun des boutons n'est «cliqué».

s'ils sont cliqués, ils ont la préférence et sa valeur est transmise.

gcb
la source
2
Mais ce comportement est-il spécifié par la norme? Et peut-on faire confiance aux navigateurs pour l'implémenter correctement?
Stewart
bonne question. je l'ai testé pour fonctionner sur tous les navigateurs A (à partir de la matrice de navigateur yui) et c'est le plus cohérent que je puisse obtenir dans la vie réelle (quelles que soient les normes spécifiées)
gcb
@yellowandred que se passe-t-il?
jettera
1
mon erreur, j'ai changé {type = "hidden"} et je n'ai pas remarqué les mêmes valeurs de "nom".
Jaune et rouge
Cela ne fonctionne pas pour moi dans IE11 ou Fx30. La valeur est toujours définie sur la valeur masquée.
Jamie Kitson
0

Une autre solution que j'ai utilisée consiste à avoir un seul bouton dans le formulaire et à simuler les autres boutons.

Voici un exemple:

<form>
  <label for="amount">Amount of items</label>
  <input id="amount" type="text" name="amount" />
  <span id="checkStock" class="buttonish">Check stock</span>
  <button type="submit" name="action" value="order">Place order</button>
</form>

Je stylise ensuite les éléments span pour ressembler à un bouton. Un écouteur JS observe l'étendue et effectue l'opération souhaitée une fois cliqué.

Pas nécessairement adapté à toutes les situations, mais au moins c'est assez facile à faire.

Johan Fredrik Varen
la source
1
Quelle est la solution de rechange pour les utilisateurs qui ont désactivé JS?
Stewart
Les utilisateurs avec JS désactivé ne pourront pas utiliser Internet, c'est aussi simple que cela de nos jours.
Christoffer Bubach
0

De la spécification HTML 4 :

Si un formulaire contient plusieurs boutons d'envoi, seul le bouton d'envoi activé réussit.

Cela signifie qu'étant donné plus d'un bouton de soumission et aucun activé (cliqué), aucun ne devrait réussir.

Et je dirais que cela a du sens: imaginez un formulaire énorme avec plusieurs boutons de soumission. En haut, il y a un bouton "supprimer cet enregistrement", puis de nombreuses entrées suivent et en bas il y a un bouton "mettre à jour cet enregistrement". Un utilisateur qui appuie sur Entrée alors qu'il se trouve dans un champ au bas du formulaire ne soupçonnera jamais qu'il frappe implicitement le "supprimer cet enregistrement" en haut.

Par conséquent, je pense que ce n'est pas une bonne idée d'utiliser le premier bouton ou tout autre bouton que l'utilisateur ne définit pas (cliquez) sur un. Néanmoins, les navigateurs le font bien sûr.

Christopher K.
la source
-2

EDIT: Désolé, en écrivant cette réponse, je pensais à soumettre des boutons dans le sens général. La réponse ci-dessous ne concerne pas plusieurs type="submit"boutons, car elle n'en laisse qu'un type="submit"et change l'autre type="button". Je laisse la réponse ici comme référence au cas où aide quelqu'un qui peut changer le typedans sa forme:

Pour déterminer le bouton sur lequel vous appuyez lorsque vous appuyez sur Entrée, vous pouvez le marquer avec type="submit"et les autres boutons les marquent avec type="button". Par exemple:

<input type="button" value="Cancel" />
<input type="submit" value="Submit" />
Jesús Carrera
la source
Tapez "bouton" n'entraîne pas l'envoi du formulaire. Il est utilisé uniquement pour déclencher des scripts côté client.
Stewart
exactement, c'est pourquoi le navigateur l'ignore lorsque vous appuyez sur Entrée, et celui de type "soumettre" est exécuté. Vous pouvez utiliser celui de type "bouton" pour faire autre chose sur clic par exemple. Veuillez supprimer le vote négatif.
Jesús Carrera
La question portait sur plusieurs soumettre des boutons.
Stewart
Mon exemple est totalement valable pour plusieurs boutons d'envoi. Celui avec le type "soumettre" est celui déterminé par le navigateur pour agir comme pressé (ce qui était la question). Ensuite, avec l'autre bouton, vous pouvez également l'utiliser comme bouton d'envoi en utilisant des événements JavaScript comme l'événement onClick que vous avez dans votre exemple. Ma réponse est donc pertinente et ne mérite pas un vote négatif. Veuillez le retirer.
Jesús Carrera
1
Si vous vouliez suggérer de supprimer l'ambiguïté en faisant un seul d'entre eux taper "soumettre" et en utilisant JavaScript pour implémenter les autres boutons, alors c'est un doublon de la réponse du graphique et en tant que tel a déjà été rejeté comme une mauvaise idée.
Stewart