Como é determinado o botão de envio padrão em um formulário HTML?

210

Se um formulário for enviado, mas não por um botão específico, como

  • pressionando Enter
  • usando HTMLFormElement.submit()em JS

como um navegador deve determinar qual dos vários botões de envio, se houver, usar como o pressionado?

Isso é significativo em dois níveis:

  • chamando um onclickmanipulador de eventos anexado a um botão de envio
  • os dados enviados de volta ao servidor da web

Minhas experiências até agora mostraram que:

  • ao pressionar Enter, Firefox, Opera e Safari usam o primeiro botão de envio no formulário
  • ao pressionar Enter, o IE usa o primeiro botão de envio ou nenhum, dependendo das condições que não consegui descobrir
  • todos esses navegadores não usam nenhum para envio de JS

O que diz o padrão?

Se ajudar, aqui está o meu código de teste (o PHP é relevante apenas para o meu método de teste, não para a minha pergunta)

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

Respostas:

115

Se você enviar o formulário via Javascript ( formElement.submit()ou seja, algo equivalente), nenhum dos botões de envio será considerado bem-sucedido e nenhum de seus valores será incluído nos dados enviados. (Observe que, se você enviar o formulário usando submitElement.click(), o envio para o qual você fez referência é considerado ativo; isso não é da competência da sua pergunta, já que aqui o botão enviar é inequívoco, mas achei que o incluiria para pessoas que leem a primeira parte e se perguntam como obter êxito em um botão de envio por meio do envio de formulários JS. É claro que os manipuladores de envio de formulários ainda serão acionados dessa maneira, enquanto não o farão, form.submit()então isso é outra chaleira de peixe ...)

Se o formulário for enviado pressionando Enter enquanto estiver em um campo que não seja da área de texto, o agente do usuário deverá decidir o que deseja aqui. As especificações não dizem nada sobre o envio de um formulário usando a tecla enter enquanto estiver em um campo de entrada de texto (se você tabular para um botão e ativá-lo usando espaço ou qualquer outra coisa, não haverá problema, pois esse botão de envio específico é usado sem ambiguidade). Tudo o que diz é que um formulário deve ser enviado quando um botão de envio é ativado, nem é um requisito que pressionar enter, por exemplo, uma entrada de texto enviará o formulário.

Eu acredito que o Internet Explorer escolhe o botão enviar que aparece primeiro na fonte; Sinto que o Firefox e o Opera escolhem o botão com o menor índice de tabulação, voltando ao primeiro definido se nada mais estiver definido. Há também algumas complicações sobre se os envios têm um atributo de valor não padrão IIRC.

O ponto a ser retirado é que não existe um padrão definido para o que acontece aqui e é inteiramente por capricho do navegador - portanto, na medida do possível, faça o que estiver fazendo, tente evitar confiar em qualquer comportamento específico. Se você realmente deve saber, provavelmente poderá descobrir o comportamento das várias versões do navegador, mas, quando investiguei isso há algum tempo, havia algumas condições bastante complicadas (que obviamente estão sujeitas a alterações nas novas versões do navegador) e eu aconselho você evitá-lo, se possível!

Andrzej Doyle
fonte
1
Re para 2: Minha pergunta básica foi se existe algo em alguma especificação com o efeito "Se o navegador fornecer algum meio de enviar um formulário sem especificar um controle de envio ...". Mas deduzo da resposta de David que não há. Meu argumento sobre o IE era que, às vezes, pressionar Enter envia um formulário e não define nenhum botão de envio. Uma consequência é que, se você tiver vários formulários enviando para o mesmo script, não poderá confiar nos botões de envio para distingui-los. Isso surgiu no projeto em que estou trabalhando.
285 Stewart
1
Seção de suporte de primeiro parágrafo muito útil - obrigado.
Rbassett
63

O HTML 4 não o torna explícito. O rascunho de trabalho atual do HTML5 especifica que o primeiro botão de envio deve ser o padrão :

O formbotão padrão de um elemento é o primeiro botão de envio em ordem de árvore cujo proprietário do formulário é esse formelemento.

Se o agente do usuário suportar a permissão para o usuário enviar um formulário implicitamente (por exemplo, em algumas plataformas, pressionar a tecla "enter" enquanto um campo de texto estiver focado no envio implicitamente do formulário), faça isso para um formulário cujo botão padrão tenha uma ativação definida o comportamento deve fazer com que o agente do usuário execute etapas de ativação de clique sintético nesse botão padrão .

Quentin
fonte
Ok, é isso que deveria fazer. Eu posso usar?
Pacerier 16/10
Fico feliz em ver "envio implícito" em HTML5.
Clint Pachl
61

O Andrezj praticamente conseguiu isso ... mas aqui está uma solução fácil para vários navegadores.

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

e refatorar para isso:

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

Como a especificação do W3C indica que vários botões de envio são válidos, mas omite orientações sobre como o agente do usuário deve lidar com eles, os fabricantes de navegadores são deixados de implementar como acharem melhor. Descobri que eles enviarão o primeiro botão de envio no formulário ou o próximo botão de envio após o campo do formulário atualmente em foco.

Infelizmente, basta adicionar um estilo de exibição: nenhum; não funcionará porque a especificação W3C indica que qualquer elemento oculto deve ser excluído das interações do usuário. Então, esconda-o à vista de todos!

Acima está um exemplo da solução que acabei colocando em produção. Pressionar a tecla Enter aciona o envio padrão do formulário como um comportamento esperado, mesmo quando outros valores não padrão estão presentes e precedem o botão de envio padrão no DOM. Bônus para interação mouse / teclado com entradas explícitas do usuário, evitando manipuladores javascript.

Nota: percorrer o formulário não exibirá o foco de nenhum elemento visual, mas fará com que o botão invisível seja selecionado. Para evitar esse problema, basta definir os atributos tabindex adequadamente e omitir um atributo tabindex no botão de envio invisível. Embora possa parecer incorreto promover esses estilos como importantes, eles devem impedir que qualquer estrutura ou estilo de botão existente interfira com essa correção. Além disso, esses estilos inline são definitivamente ruins, mas estamos provando conceitos aqui ... não escrevendo código de produção.

James
fonte
5
Obrigado. Tropecei na tela: nenhum problema também. Em vez de minimizar o botão, você também pode movê-lo para fora da página, colocando-o com a <div style="position: fixed; left: -1000px; top: -1000px />. Só mais uma coisa a se preocupar: apenas minimizar ou afastar o botão do fluxo da página com CSS pode trazer problemas de WAI, porque os leitores de tela ainda verão o botão padrão.
Stefan Haberl 28/03
2
OBRIGADO @James por esta resposta detalhada.
18712 Nathan As
1
isso funcionou para mim, obrigado. Eu tive que adicionar isso para a borda do botão: none; para fazê-lo desaparecer completamente
Macumbaomuerte 13/05
4
Você pode apenas definir o atributo tabindex do botão escondido para -1
tvanc
2
O que é isso <input/>lá em cima?
Sz.
16

Eu acho que este post ajudaria se alguém quiser fazer isso com o jQuery:

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

A solução básica é:

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

e outro que eu gostei foi:

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
fonte
jQuery.Event sempre tem seu .wichatributo. Também não há necessidade de voltar ao DOM .keyCode(ou .charCodepara esse assunto).
Arjan
Outra questão sobre o jQuery aqui é a consulta dupla desnecessária. Como não há necessidade de retornar verdadeiro, além disso, a versão do jQuery fica muito clara e falta de integração das reduções recomendadas por Arjan antes.
soletan 26/09/12
Usei o pressionamento de tecla em vez do pressionamento de tecla (evite esperar pela inserção de teclas) e adicionei e.preventDefault();o início desta função para evitar a execução do botão padrão "real". Funciona muito bem.
Matt Roy
6

Eu tinha um formulário com 11 botões de envio e ele sempre usava o primeiro botão de envio quando o usuário pressionava enter. Li em outro lugar que não é uma boa ideia (prática recomendada) ter mais de um botão de envio em um formulário, e a melhor maneira de fazer isso é ter o botão que você deseja como padrão, como o único botão de envio no formulário. Os outros botões devem ser transformados em "TYPE = BUTTON" e um evento onClick adicionado que chama sua própria rotina de envio em Javascript. Algo assim :-

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

Aqui, button3 é o padrão e, embora você esteja enviando programaticamente o formulário com os outros botões, a rotina mjsubmit os valida. HTH.

gráfico
fonte
19
Péssima ideia. Seu formulário não funcionará com o JS desativado. JS deve ser usado discretamente.
BalusC
7
Pouquíssimos navegadores têm o JS desativado por padrão, mesmo os de iPods! No formulário que eu estava citando, JS off o tornaria inútil! Tanta coisa depende do JS em execução.
gráfico
7
Desligado por padrão ou não, aqueles com o JS desativado não precisam ter que ativá-lo apenas para contornar um problema tão bobo como esse.
Stewart
2
que tipo de formulário possui 11 botões de envio ?? ( curiosidade )
Ben
2
A maior razão que vejo validar discreto Javascript é que intrusivos Javascript tende a ser trabalhosa. Não sei como qualificar o que estou pensando, mas quando vejo páginas absolutamente dependentes de JS, também aposto que posso fazer algumas coisas complicadas com o meu DOM e fazer algo não intencional no servidor.
Samantha Branham
6

Agora isso pode ser resolvido 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 */
}

Explaination
Usando a caixa de flex, podemos inverter a ordem dos elementos em um recipiente que usos display: flexutilizando também a regra CSS: flex-direction: row-reverse. Isso não requer CSS ou elementos ocultos. Para navegadores mais antigos que não oferecem suporte ao flexbox, eles ainda recebem uma solução viável, mas os elementos não serão revertidos.

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

Godwin
fonte
2

Eu lutei com a mesma pergunta, já que eu tinha o botão enviar no meio do qual o redirecionado enviava para outra página, da seguinte forma:

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

Quando o usuário pressionou a tecla Enter, esse botão foi clicado em vez de outro botão de envio.

Então, eu fiz alguns testes primitivos criando um from com vários botões de envio e diferentes opções de visibilidade e um evento onclick alertando qual botão foi clicado: https://jsfiddle.net/aqfy51om/1/

Navegadores e sistemas operacionais que usei para testar:

JANELAS

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

OSX

  • Google Chrome 43
  • Safari 7.1.6

A maioria desses navegadores clicou no primeiro botão, apesar das opções de visibilidade aplicadas, exceto o IE e o Safari, que clicaram no terceiro botão, que é "visível" dentro do contêiner "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>

Então, minha sugestão, que eu vou usar, é:

Se você formar vários botões de envio com significado diferente, inclua o botão de envio com a ação padrão no início do formulário, que é:

  1. Totalmente visível
  2. Embrulhado em um recipiente com style="width: 0; height: 0; overflow: hidden;"

EDIT Outra opção pode ser o deslocamento do botão (ainda no começo do) style="position: absolute; left: -9999px; top: -9999px;", tentei no IE - funcionou, mas não tenho idéia do que mais ele pode estragar, por exemplo, impressão ..

Kristian
fonte
1

Dos seus comentários:

Uma consequência é que, se você tiver vários formulários enviando para o mesmo script, não poderá confiar nos botões de envio para distingui-los.

Eu largo um <input type="hidden" value="form_name" />em cada formulário.

Se estiver enviando com javascript: adicione eventos de envio aos formulários, não clique nos eventos nos respectivos botões. Usuários saavy não tocam o mouse com muita frequência.

Ryan Florence
fonte
De fato, mas que implora a pergunta: stackoverflow.com/questions/541869/...
Stewart
1

Quando você tem vários botões de envio em um único formulário e um usuário pressiona a tecla ENTER para enviar o formulário a partir de um campo de texto, esse código substitui a funcionalidade padrão, chamando o evento de envio no formulário a partir do evento de pressionamento de tecla. Aqui está esse 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
fonte
Simples e corrige o problema
toddmo
1

Estranho que o primeiro botão Enter vá sempre para o primeiro botão, independentemente de estar visível ou não, por exemplo, usando jquery show/hide(). A adição de atributo .attr('disabled', 'disabled')impede o recebimento completo do botão Enter submit. É um problema, por exemplo, ao ajustar a visibilidade do botão Inserir / Editar + Excluir nas caixas de diálogo de registro. Achei menos simples e simples colocar o Edit na frente de 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>

e use o 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
fonte
0

minha receita:

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

Ele enviará o campo oculto padrão se nenhum dos botões for 'clicado'.

se clicam, têm preferência e seu valor é passado.

gcb
fonte
2
Mas esse comportamento é especificado pelo padrão? E os navegadores podem ser confiáveis ​​para implementá-lo corretamente?
8309 Stewart
boa pergunta. Eu testei-o para o trabalho em todos os navegadores A (a partir da matriz navegador yui) e é o mais consitent eu poderia obter na vida real (independentemente do que os padrões especificar)
gcb
@yellowandred o que acontece? vai dar uma olhada mais tarde
gcb 28/08
1
meu erro, mudei {type = "hidden"} e não notei os mesmos valores de 'nome'.
Amarelo e Vermelho
Isso não funciona para mim no IE11 ou no Fx30. O valor é sempre definido como o valor oculto.
precisa
0

Outra solução que usei é ter apenas um botão no formulário e falsificar os outros botões.

Aqui está um exemplo:

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

Eu, então, denomino os elementos de amplitude para parecerem um botão. Um ouvinte JS observa a extensão e executa a operação desejada uma vez clicada.

Não necessariamente adequado para todas as situações, mas pelo menos é bem fácil de fazer.

Johan Fredrik Varen
fonte
1
Qual é o substituto para usuários com o JS desativado?
21311 Stewart
Usuários com JS desabilitado não poderão usar a Internet, é tão simples quanto isso hoje em dia.
Christoffer Bubach 9/16
0

A partir da especificação do HTML 4 :

Se um formulário contiver mais de um botão de envio, apenas o botão de envio ativado será bem-sucedido.

Isso significa que, dado mais de um botão de envio e nenhum ativado (clicado), nenhum deve ser bem-sucedido.

E eu argumentaria que isso faz sentido: imagine um formulário enorme com vários botões de envio. Na parte superior, há um botão "excluir este registro", depois várias entradas e, na parte inferior, um botão "atualizar este registro". Um usuário pressionando enter enquanto estiver em um campo na parte inferior do formulário nunca suspeitaria que ele implicitamente clicou no botão "excluir este registro" da parte superior.

Portanto, acho que não é uma boa ideia usar o primeiro ou qualquer outro botão que o usuário não defina (clique). No entanto, os navegadores estão fazendo isso, é claro.

Christopher K.
fonte
-2

EDIT: Desculpe, ao escrever esta resposta, eu estava pensando em enviar botões no sentido geral. A resposta abaixo não é sobre vários type="submit"botões, pois deixa apenas um type="submit"e altera o outro para type="button". Deixo a resposta aqui como referência, caso ajude alguém que possa alterar a typeforma:

Para determinar qual botão é pressionado ao pressionar Enter, você pode marcá-lo type="submit"e os outros botões os marcam type="button". Por exemplo:

<input type="button" value="Cancel" />
<input type="submit" value="Submit" />
Jesús Carrera
fonte
Digite "botão" não faz com que o formulário seja enviado. É usado apenas para disparar scripts do lado do cliente.
Stewart
exatamente, é por isso que o navegador o ignora quando você pressiona enter e o tipo "submit" é executado. Você pode usar aquele com o tipo "botão" para fazer qualquer outra coisa ao clicar, por exemplo. Por favor, remova o voto negativo.
Jesús Carrera
A pergunta era sobre vários botões de envio .
Stewart
Meu exemplo é totalmente válido para vários botões de envio. Aquele com o tipo "enviar" é aquele determinado pelo navegador para agir como pressionado (que é a questão). Em seguida, com o outro botão, você pode usá-lo como botão enviar, além de usar eventos JavaScript como o evento onClick que você possui no seu exemplo. Portanto, minha resposta é relevante e não merece voto negativo. Por favor, remova-o.
Jesús Carrera
1
Se o seu objetivo era sugerir remover a ambiguidade, fazendo apenas um deles digitar "enviar" e usar JavaScript para implementar os outros botões, isso é uma duplicata da resposta do gráfico e, como tal, já foi descartada como uma má idéia.
21413 Stewart