¿Cómo se determina el botón de envío predeterminado en un formulario HTML?

210

Si se envía un formulario pero no mediante un botón específico, como

  • presionando Enter
  • utilizando HTMLFormElement.submit()en JS

¿Cómo se supone que un navegador determina cuál de los múltiples botones de envío, si los hay, usar como el que se presiona?

Esto es significativo en dos niveles:

  • llamar a un onclickcontrolador de eventos adjunto a un botón de envío
  • los datos enviados de vuelta al servidor web

Mis experimentos hasta ahora han demostrado que:

  • al presionar Enter, Firefox, Opera y Safari usan el primer botón de enviar en el formulario
  • Al presionar Enter, IE utiliza el primer botón de envío o ninguno, dependiendo de las condiciones que no he podido resolver
  • todos estos navegadores no usan ninguno para enviar JS

¿Qué dice el estándar?

Si eso ayudara, aquí está mi código de prueba (el PHP es relevante solo para mi método de prueba, no para mi pregunta en sí)

<!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
fuente

Respuestas:

115

Si envía el formulario a través de Javascript (es decir, formElement.submit()o algo equivalente), ninguno de los botones de envío se considera exitoso y ninguno de sus valores se incluye en los datos enviados. (Tenga en cuenta que si envía el formulario mediante el uso submitElement.click(), el envío al que hizo referencia se considera activo; esto realmente no corresponde al alcance de su pregunta, ya que aquí el botón de envío no es ambiguo, pero pensé que lo incluiría para las personas que leen la primera parte y se preguntan cómo hacer que un botón de envío sea exitoso a través del envío del formulario JS. Por supuesto, los controladores de envío del formulario seguirán disparando de esta manera mientras que no lo harían, form.submit()así que esa es otra caldera de pescado ...)

Si el formulario se envía presionando Intro mientras está en un campo que no es de área de texto, entonces el agente de usuario debe decidir qué quiere aquí. Las especificaciones no dicen nada acerca de enviar un formulario utilizando la tecla Intro mientras está en un campo de entrada de texto (si presiona un botón y lo activa usando el espacio o lo que sea, entonces no hay ningún problema ya que ese botón de envío específico se usa sin ambigüedades). Todo lo que dice es que se debe enviar un formulario cuando se activa un botón de envío, ni siquiera es un requisito presionar enter, por ejemplo, una entrada de texto enviará el formulario.

Creo que Internet Explorer elige el botón de envío que aparece primero en la fuente; Tengo la sensación de que Firefox y Opera eligen el botón con el índice de tabulación más bajo, volviendo al primero definido si no se define nada más. También hay algunas complicaciones con respecto a si los envíos tienen un atributo de valor no predeterminado IIRC.

El punto a eliminar es que no hay un estándar definido para lo que sucede aquí y es completamente a voluntad del navegador, por lo que, en la medida de lo posible en lo que sea que esté haciendo, trate de evitar depender de un comportamiento en particular. Si realmente debe saberlo, probablemente pueda descubrir el comportamiento de las diferentes versiones del navegador, pero cuando investigué esto hace un tiempo hubo algunas condiciones bastante complicadas (que por supuesto están sujetas a cambios con las nuevas versiones del navegador) y le aconsejaría ¡para evitarlo si es posible!

Andrzej Doyle
fuente
1
Re para 2: Mi pregunta básica era si hay algo en alguna especificación en el sentido de "Si el navegador proporciona algún medio para enviar un formulario sin especificar un control de envío ...". Pero deduzco de la respuesta de David que no hay. Mi punto sobre IE fue que a veces al presionar Enter se envía un formulario y no se establece ningún botón de envío. Una consecuencia es que, si tiene varios formularios que se envían al mismo script, no puede confiar en los botones de envío para distinguirlos. Esto surgió en el proyecto en el que estoy trabajando.
Stewart
1
Muy útil sección de paréntesis de primer párrafo - gracias.
rbassett
63

HTML 4 no lo hace explícito. El borrador de trabajo HTML5 actual especifica que el primer botón de envío debe ser el predeterminado :

formEl botón predeterminado de un elemento es el primer botón de envío en orden de árbol cuyo propietario de formulario es ese formelemento.

Si el agente de usuario admite permitir que el usuario envíe un formulario implícitamente (por ejemplo, en algunas plataformas presionando la tecla "enter" mientras un campo de texto está enfocado, envía implícitamente el formulario), y luego lo hace para un formulario cuyo botón predeterminado tiene una activación definida El comportamiento debe hacer que el agente de usuario ejecute pasos de activación de clic sintético en ese botón predeterminado .

Quentin
fuente
Ok, eso es lo que se supone que debe hacer. ¿Puedo usar?
Pacerier
Me alegra ver "envío implícito" en HTML5.
Clint Pachl
61

Andrezj prácticamente lo tiene clavado ... pero aquí hay una solución fácil para varios navegadores.

Toma una forma como esta:

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

y refactorizar esto:

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

Dado que la especificación del W3C indica que varios botones de envío son válidos, pero omite la orientación sobre cómo debe manejarlos el agente de usuario, los fabricantes de navegadores deben implementarlos como mejor les parezca. Descubrí que enviarán el primer botón de envío en el formulario o enviarán el siguiente botón de envío después del campo del formulario que actualmente tiene el foco.

Desafortunadamente, simplemente agregando un estilo de visualización: ninguno; no funcionará porque la especificación W3C indica que cualquier elemento oculto debe excluirse de las interacciones del usuario. ¡Así que escóndelo a la vista!

Arriba hay un ejemplo de la solución que terminé poniendo en producción. Al presionar la tecla Intro, el envío de formulario predeterminado es el comportamiento esperado, incluso cuando otros valores no predeterminados están presentes y preceden al botón de envío predeterminado en el DOM. Bonificación por la interacción del mouse / teclado con entradas explícitas del usuario mientras se evitan los controladores javascript.

Nota: la tabulación a través del formulario no mostrará el foco de ningún elemento visual, pero aún hará que se seleccione el botón invisible. Para evitar este problema, simplemente configure los atributos tabindex en consecuencia y omita un atributo tabindex en el botón de envío invisible. Si bien puede parecer fuera de lugar promover estos estilos como importantes, deben evitar que cualquier marco o estilos de botones existentes interfieran con esta solución. Además, esos estilos en línea son definitivamente deficientes, pero aquí estamos probando conceptos ... no escribiendo código de producción.

James
fuente
55
Gracias. Tropecé con la pantalla: ningún problema también. En lugar de minimizar el botón, también puede moverlo fuera de la página rodeándolo con un <div style="position: fixed; left: -1000px; top: -1000px />. Solo una cosa más para preocuparse: solo minimizar o alejar el botón del flujo de la página con CSS puede generar problemas de WAI, porque los lectores de pantalla aún verán el botón predeterminado.
Stefan Haberl
2
GRACIAS @ James por esta respuesta detallada.
Nathan
1
Esto funcionó para mí, gracias. Tuve que agregar esto para el borde del botón: ninguno; hacer que desaparezca completamente
Macumbaomuerte
44
Simplemente puede establecer el atributo tabindex del botón oculto en -1
tvanc
2
¿Para qué es eso <input/>?
Sz.
16

Creo que esta publicación ayudaría si alguien quiere hacerlo con jQuery:

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

La solución básica es:

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

y otro que me gustó fue:

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
fuente
jQuery.Event siempre tiene su .wichatributo. Tampoco es necesario recurrir a DOM .keyCode(o .charCodepara el caso).
Arjan
Otro problema con respecto a jQuery aquí es la doble consulta innecesaria. Dado que no hay necesidad de devolver verdadero, además, la versión jQuery se vuelve realmente clara y corta al integrar las reducciones recomendadas por Arjan anteriormente.
soletan
Utilicé keydown en lugar de presionar teclas (evite esperar la activación de teclas) y agregué e.preventDefault();al comienzo de esta función para evitar la ejecución del botón predeterminado "real". Funciona muy bien
Matt Roy
6

Tenía un formulario con 11 botones de envío, y siempre usaría el primer botón de envío cuando el usuario presionó enter. Leí en otra parte que no es una buena idea (mala práctica) tener más de un botón de envío en un formulario, y la mejor manera de hacerlo es tener el botón que desea por defecto, como el único botón de envío en el formulario. Los otros botones deben estar en "TYPE = BUTTON" y se debe agregar un evento onClick que llame a su propia rutina de envío en Javascript. Algo como esto :-

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

Aquí, button3 es el valor predeterminado, y aunque envía el formulario mediante programación con los otros botones, la rutina mjsubmit los valida. HTH

gráfico
fuente
19
Mala idea. Su formulario no funcionará con JS deshabilitado. JS debe usarse discretamente.
BalusC
77
Muy pocos navegadores tienen JS desactivado por defecto, ¡incluso los que están en iPods! En el formulario que estaba citando, ¡JS off lo haría inútil! Tanto depende de JS corriendo.
gráfico
77
Apagado por defecto o no, aquellos que tienen JS deshabilitado no deberían tener que encenderlo solo para evitar un inconveniente tan tonto como este.
Stewart
2
¿Qué tipo de formulario tiene 11 botones de envío? ( curiosidad )
Ben
2
La razón más grande que veo validar discreta Javascript es que molesta Javascript tiende a ser más incómoda. No estoy seguro de cómo calificar lo que estoy pensando, pero cuando veo páginas que dependen absolutamente de JS, también apuesto a que puedo hacer algunas cosas difíciles con mi DOM y hacer algo no deseado en el servidor.
Samantha Branham
6

Esto ahora se puede resolver usando 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 */
}

Explicación
Utilización del cuadro de flexión, se puede invertir el orden de los elementos en un recipiente que los usos display: flextambién usando la regla CSS: flex-direction: row-reverse. Esto no requiere CSS ni elementos ocultos. Para los navegadores más antiguos que no son compatibles con flexbox, aún obtienen una solución viable, pero los elementos no se revertirán.

Demo
http://codepen.io/Godwin/pen/rLVKjm

Godwin
fuente
2

Luché con la misma pregunta ya que tenía el botón de envío en el medio del cual se envió el envío redirigido a otra página, así:

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

Cuando el usuario presionó la tecla Intro, se hizo clic en este botón en lugar de otro botón de envío.

Así que hice algunas pruebas primitivas creando un desde múltiples botones de envío y diferentes opciones de visibilidad y un evento de clic que alertaba en qué botón se hizo clic: https://jsfiddle.net/aqfy51om/1/

Navegadores y sistemas operativos que utilicé para las pruebas:

Ventanas

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

OSX

  • Google Chrome 43
  • Safari 7.1.6

La mayoría de estos navegadores hicieron clic en el primer botón a pesar de las opciones de visibilidad aplicadas, excepto IE y Safari, que hicieron clic en el tercer botón, que es "visible" dentro del contenedor "oculto":

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

Entonces, mi sugerencia, que voy a usar yo mismo, es:

Si su formulario tiene varios botones de envío con un significado diferente, incluya el botón de envío con acción predeterminada al comienzo del formulario, que es:

  1. Totalmente visible
  2. Envuelto en un recipiente con style="width: 0; height: 0; overflow: hidden;"

EDITAR Otra opción podría ser compensar el botón (aún al comienzo de from) style="position: absolute; left: -9999px; top: -9999px;", solo lo intenté en IE, funcionó, pero no tengo idea de qué más puede arruinar, por ejemplo, imprimir ...

Kristian
fuente
1

De tus comentarios:

Una consecuencia es que, si tiene varios formularios que se envían al mismo script, no puede confiar en los botones de envío para distinguirlos.

Dejo caer un <input type="hidden" value="form_name" />en cada formulario.

Si se envía con javascript: agregue eventos de envío a formularios, no haga clic en eventos en sus botones. Los usuarios de Saavy no tocan el mouse con mucha frecuencia.

Ryan Florence
fuente
De hecho, pero eso plantea esta pregunta: stackoverflow.com/questions/541869/…
Stewart
1

Cuando tiene varios botones de envío en un solo formulario y un usuario presiona la tecla ENTRAR para enviar el formulario desde un campo de texto, este código anula la funcionalidad predeterminada, llamando al evento de envío en el formulario desde el evento de presión de tecla. Aquí está ese código:

$('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
fuente
Simple y soluciona el problema
toddmo
1

Es extraño que el primer botón Enter vaya siempre al primer botón, independientemente de si es visible o no, por ejemplo, usando jquery show/hide(). Agregar atributo .attr('disabled', 'disabled')impide recibir el botón Enviar enviar completamente. Es un problema, por ejemplo, al ajustar la visibilidad del botón Insertar / Editar + Eliminar en los cuadros de diálogo de grabación. Encontré menos hackeo y colocación simple Editar delante de Insertar

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

y usa el código 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
fuente
0

mi receta:

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

Enviará el campo oculto predeterminado si no se hace clic en ninguno de los botones.

si se hace clic, tienen preferencia y se pasa su valor.

gcb
fuente
2
Pero, ¿es este comportamiento especificado por el estándar? ¿Y se puede confiar en los navegadores para implementarlo correctamente?
Stewart
buena pregunta. Lo he probado para trabajar en todos los navegadores A (de matriz navegador Yui) y es el más consitent podría conseguir en la vida real (independientemente de lo que especifican los estándares)
GCB
@yellowandred ¿qué pasa?
echaremos
1
mi error, cambié {type = "hidden"} y no noté los mismos valores de 'nombre'.
Amarillo y rojo
Esto no funciona para mí en IE11 o Fx30. El valor siempre se establece en el valor oculto.
Jamie Kitson
0

Otra solución que he usado es tener solo un botón en el formulario y falsificar los otros botones.

Aquí hay un ejemplo:

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

Luego estilo los elementos span para que parezca un botón. Un oyente JS observa el lapso y realiza la operación deseada una vez que se hace clic.

No necesariamente es adecuado para todas las situaciones, pero al menos es bastante fácil de hacer.

Johan Fredrik Varen
fuente
1
¿Cuál es el respaldo para los usuarios que tienen JS deshabilitado?
Stewart
Los usuarios con JS deshabilitado no podrán usar Internet, es tan simple como eso hoy en día.
Christoffer Bubach
0

De la especificación HTML 4 :

Si un formulario contiene más de un botón de envío, solo el botón de envío activado es exitoso.

Esto significa que, dado más de 1 botón de envío y ninguno activado (hecho clic), ninguno debería tener éxito.

Y diría que esto tiene sentido: imagina una forma enorme con múltiples botones de envío. En la parte superior, hay un botón "eliminar este registro", luego siguen muchas entradas y en la parte inferior hay un botón "actualizar este registro". Un usuario que presiona enter mientras está en un campo en la parte inferior del formulario nunca sospechará que golpea implícitamente el "borrar este registro" desde la parte superior.

Por lo tanto, creo que no es una buena idea usar el primer botón o cualquier otro botón que el usuario no defina (haga clic). Sin embargo, los navegadores lo están haciendo, por supuesto.

Christopher K.
fuente
-2

EDITAR: Lo siento, al escribir esta respuesta estaba pensando en enviar botones en el sentido general. La respuesta a continuación no se trata de varios type="submit"botones, ya que deja solo uno type="submit"y cambia el otro a type="button". Dejo la respuesta aquí como referencia en caso de que ayude a alguien que pueda cambiar eltype forma:

Para determinar qué botón se presiona al presionar enter, puede marcarlo con type="submit", y los otros botones lo marcan con type="button". Por ejemplo:

<input type="button" value="Cancel" />
<input type="submit" value="Submit" />
Jesús Carrera
fuente
Escriba "botón" no hace que el formulario se envíe. Se usa solo para activar scripts del lado del cliente.
Stewart
exactamente, es por eso que el navegador lo omite cuando presiona enter, y el que tiene el tipo "enviar" se ejecuta. Puede usar el que tiene el tipo "botón" para hacer cualquier otra cosa al hacer clic, por ejemplo. Por favor, elimine el voto negativo.
Jesús Carrera
La pregunta era sobre múltiples botones de envío .
Stewart
Mi ejemplo es totalmente válido para múltiples botones de envío. El que tiene el tipo "enviar" es el que determina el navegador para actuar como presionado (que es de lo que se trataba la pregunta). Luego, con el otro botón, puede usarlo como botón de envío y usar eventos de JavaScript como el evento onClick que tiene en su ejemplo. Entonces mi respuesta es relevante y no merece un voto negativo. Por favor quítelo.
Jesús Carrera
1
Si su punto era sugerir eliminar la ambigüedad haciendo que solo uno de ellos escriba "enviar" y use JavaScript para implementar los otros botones, entonces este es un duplicado de la respuesta del gráfico y, como tal, ya se ha descartado como una mala idea.
Stewart