jQuery: como obter qual botão foi clicado após o envio do formulário?

227

eu tenho um .submit() evento configurado para envio de formulários. Também tenho vários formulários na página, mas apenas um aqui para este exemplo. Gostaria de saber em qual botão enviar foi clicado sem aplicar um .click()evento a cada um.

Aqui está a configuração:

<html>
<head>
  <title>jQuery research: forms</title>
  <script type='text/javascript' src='../jquery-1.5.2.min.js'></script>
  <script type='text/javascript' language='javascript'>
      $(document).ready(function(){
          $('form[name="testform"]').submit( function(event){ process_form_submission(event); } );
      });
      function process_form_submission( event ) {
          event.preventDefault();
          //var target = $(event.target);
          var me = event.currentTarget;
          var data = me.data.value;
          var which_button = '?';       // <-- this is what I want to know
          alert( 'data: ' + data + ', button: ' + which_button );
      }
  </script>
</head>
<body>
<h2>Here's my form:</h2>
<form action='nothing' method='post' name='testform'>
  <input type='hidden' name='data' value='blahdatayadda' />
  <input type='submit' name='name1' value='value1' />
  <input type='submit' name='name2' value='value2' />
</form>
</body>
</html>

Exemplo ao vivo no jsfiddle

Além de aplicar um evento .click () em cada botão, existe uma maneira de determinar qual botão de envio foi clicado?

hawkexp
fonte
2
A ironia, é claro, é que essas informações sejam triviais para determinar o lado do servidor.
194 Neil
5
@ Neil Não se você estiver enviando o formulário via $ .ajax () e um serializeArray () no formulário.
Aaron
1
Que tal colocar um ouvinte nos botões de envio e enviar o formulário manualmente.
JMRC
1
Que tal olhar para a versão serializada dos dados do formulário e corresponder aos botões name?
Augustin Riedinger

Respostas:

235

Fiz a mesma pergunta: Como posso obter o botão que causou o envio no evento de envio do formulário?

Acabei criando esta solução e funcionou muito bem:

$(document).ready(function() {
    $("form").submit(function() { 
        var val = $("input[type=submit][clicked=true]").val();
        // DO WORK
    });
    $("form input[type=submit]").click(function() {
        $("input[type=submit]", $(this).parents("form")).removeAttr("clicked");
        $(this).attr("clicked", "true");
    });
});

No seu caso, com vários formulários, você pode precisar ajustar um pouco, mas ainda deve aplicar

caçador
fonte
11
+1 boa solução. Convém adicionar uma instrução que redefina o atributo clicado para false entre os botões, caso o envio do formulário seja tratado de maneira ajax e você queira evitar receber cliques anteriores novamente.
19411 Chandu
1
Ah eu vejo. Você está adicionando seu próprio atributo "clicado". Eu estava procurando por um booleano "clicado" e não consegui encontrar um em nenhum lugar. Eu nunca pensei em fazer um eu mesmo. Boa ideia!
precisa saber é o seguinte
9
Esteja ciente de que isso funciona apenas com elementos de entrada, não com elementos de botão.
Bob.at.Indigo.Health
1
Isso é desnecessário no Chrome com elementos de botão. Eu simplesmente manipulo o evento on submit, construo um objeto FormData a partir do formulário e ele sempre inclui o valor do botão que foi clicado. O FireFox NÃO exibe esse comportamento e, em vez disso, não envia o valor do botão. O Chrome parece estar executando algo como a solução acima automaticamente, lembrando-se de qual botão foi clicado e incluindo-o nos dados do formulário automaticamente. Obviamente, essa falta de comportamento padrão (afetando tudo em JavaScript de qualquer maneira, então não estou surpresa), torna o recurso inútil.
Triynko 30/11/2015
1
Além disso, considere adicionar o seletor "botão [type = submit]" (separado por vírgula), pois os elementos de envio não precisam ser tags de entrada.
22417
91

Eu achei que isso funcionou.

$(document).ready(function() {
    $( "form" ).submit(function () {
        // Get the submit button element
        var btn = $(this).find("input[type=submit]:focus" );
    });
}
Stan
fonte
12
esta solução funciona bem no Chrome, mas não pode funcionar no Safari, ela retornará indefinida, não o objeto de botão
Sr. Sun Lin
1
@ Krinkle Eu codifiquei assim para mostrar a maneira mais genérica possível. Eu acho que os desenvolvedores são inteligentes o suficiente para otimizá-lo ao seu gosto.
Stan
Mais limpo que as outras soluções. Ainda não encontrou uma solução que funciona quando pressionar Enter embora
andrewtweber
Essa resposta nem sempre funcionará. Na verdade, você precisa do botão clicado, não do foco (pois a ação de foco não é uma ação de clique). A resposta selecionada (do caçador) é a correta.
Vasil Popov
Eu tenho um evento de clique nos botões de envio e enviar eventos em seus formulários ... o último aciona o primeiro. Eu usei isso para descobrir a fonte do evento. Em alguns navegadores, clicando no botão, o botão fica focado; em outros, você não recebe nada. Em todos os navegadores que testei, pressionar Enter enquanto estiver em uma entrada mantém a entrada focada.
11238 notacouch
65

Isso funciona para mim:

$("form").submit(function() {
   // Print the value of the button that was clicked
   console.log($(document.activeElement).val());
}
seddonym
fonte
3
Este foi útil, para obter o iddo activeElement, eu fiz$(document.activeElement).attr('id');
Mikolaj
9
Pelo que vale a pena, você não precisa usar o jQuery para obter o ID - é ainda mais simples usar o Javascript puro:document.activeElement.id
Nick F
5
Observe que isso não parece funcionar como esperado no Safari. :(
rinogo
@rinogo: o que acontece no Safari?
Crescent Fresh
1
No Safari document.activeElementnão é a entrada. No meu caso, é uma div modal (que contém o formulário).
mgalgs
46

Quando o formulário é enviado:

  • document.activeElement fornecerá o botão de envio que foi clicado.

  • document.activeElement.getAttribute('value') fornecerá o valor desse botão.

Nick F
fonte
2
Observe que isso não parece funcionar como esperado no Safari. :(
rinogo
Eu não sei como sobre o Safari, mas no Chrome funcionou bem :) Obrigado!
Ingus
Funciona no Chrome 63 e IE11
Sir Crispalot 17/18
Eu tinha a validação discreta do jquery em execução, e isso intercepta antes disso e define a entrada inválida como activeElement, em vez do botão.
Peheje 28/09
28

Aqui está a abordagem que parece mais limpa para meus propósitos.

Primeiro, para toda e qualquer forma:

$('form').click(function(event) {
  $(this).data('clicked',$(event.target))
});

Quando esse evento de clique é acionado para um formulário, ele simplesmente registra o destino de origem (disponível no objeto de evento) para ser acessado posteriormente. É um traço bastante amplo, pois dispara para qualquer clique em qualquer lugar do formulário. Comentários de otimização são bem-vindos, mas suspeito que nunca causará problemas visíveis.

Em seguida, $('form').submit()você pode perguntar o que foi clicado pela última vez, com algo como

if ($(this).data('clicked').is('[name=no_ajax]')) xhr.abort();
Jonathan Camenisch
fonte
E se este for um evento de envio de formulário em vez de clicar?
Tony
1
@ Tony, desculpe, não vi sua pergunta antes. Esta resposta é inteiramente referente aos envios de formulário. Os botões Enviar também são clicados e, portanto, acionam o evento clique.
Jonathan Camenisch
2
A votação positiva desta resposta sobre a resposta do hunter, uma vez que os eventos de envio do formulário são acionados antes de qualquer evento filho clicar em elemento. Isso causa dificuldades quando o evento de envio do seu formulário é cancelado return false, enquanto esse método funciona corretamente, pois o clique é vinculado ao formulário em vez de seus filhos.
pospi
7
Isso falha ao lidar adequadamente com o caso em que o formulário é enviado por outros meios que não um clique (por exemplo, pressionando Enter). Quando o formulário é enviado por outros meios, é o primeiro botão de envio que acionou o evento de envio, e não o último clicado.
quietmint
Há um ponto e vírgula faltando depois ... $(event.target)), mas não pode editar porque edições deve ser> 6 caracteres
Hafenkranich
13

Uau, algumas soluções podem ficar complicadas! Se você não se importa em usar um global simples, aproveite o fato de o evento de clique no botão de entrada ser acionado primeiro. Pode-se filtrar ainda mais o seletor $ ('input') para um dos muitos formulários usando $ ('# myForm input').

    $(document).ready(function(){
      var clkBtn = "";
      $('input[type="submit"]').click(function(evt) {
        clkBtn = evt.target.id;
      });

      $("#myForm").submit(function(evt) {
        var btnID = clkBtn;
        alert("form submitted; button id=" + btnID);
      });
    });
relvado
fonte
12

Eu encontrei a melhor solução é

$(document.activeElement).attr('id')

Isso não funciona apenas em entradas, mas também em tags de botão. Também obtém o ID do botão.

Thomas Williams
fonte
Porra, eu tenho que ver se minha página funciona no Safari agora doh. Por que o Safari sempre tem que ser tão difícil. Obrigado por isso. Indo para verificar quando eu chegar em casa
Thomas Williams
Isso não está funcionando para mim no Safari. O ouvinte de clique em um botão no corpo chama .submit em um formulário. O $(document.activeElement).attr('id')retorno do manipulador de envioundefined
dyodji 30/04
7

Outra solução possível é adicionar um campo oculto no seu formulário:

<input type="hidden" id="btaction"/>

Em seguida, na função ready, adicione funções para registrar qual tecla foi pressionada:

$('form#myForm #btnSubmit').click(function() {
    $('form#myForm #btaction').val(0);
});

$('form#myForm #btnSubmitAndSend').click(function() {
    $('form#myForm #btaction').val(1);
});

$('form#myForm #btnDelete').click(function() {
    $('form#myForm #btaction').val(2);
});

Agora, no manipulador de envio de formulários, leia a variável oculta e decida com base nela:

var act = $('form#myForm #btaction').val();
wmac
fonte
6

Com base no que Stan e yann-h fizeram, esse padrão é o primeiro botão. A vantagem dessa abordagem geral é que ela captura o clique e a tecla Enter (mesmo que o foco não estivesse no botão. Se você precisar permitir a inserção no formulário, basta responder quando o botão estiver focado ( ou seja, a resposta de Stan.) No meu caso, eu queria permitir que o enter enviasse o formulário, mesmo que o foco atual do usuário estivesse na caixa de texto.

Eu também estava usando um atributo 'name' em vez de 'id', mas esta é a mesma abordagem.

var pressedButtonName =
     typeof $(":input[type=submit]:focus")[0] === "undefined" ?
     $(":input[type=submit]:first")[0].name :
     $(":input[type=submit]:focus")[0].name;
andrewmo
fonte
Então, se você mover o foco para o segundo botão com TAB e pressione RETURN ele será o padrão para o primeiro botão ...
FrancescoMM
5

Este funcionou para mim

$('#Form').submit(function(){
var btn= $(this).find("input[type=submit]:focus").val();
alert('you have clicked '+ btn);

}
Saheb Mondal
fonte
Isso merece medalha!
Martin Zvarík
4

Se o que você quer dizer com não adicionar um evento .click é que não deseja ter manipuladores separados para esses eventos, você pode manipular todos os cliques (envios) em uma função:

$(document).ready(function(){
  $('input[type="submit"]').click( function(event){ process_form_submission(event); } );
});

function process_form_submission( event ) {
  event.preventDefault();
  //var target = $(event.target);
  var input = $(event.currentTarget);
  var which_button = event.currentTarget.value;
  var data = input.parents("form")[0].data.value;
//  var which_button = '?';       // <-- this is what I want to know
  alert( 'data: ' + data + ', button: ' + which_button );
}
jcane86
fonte
Não é uma má ideia. Isso funcionaria para qualquer número de formulários na página?
precisa saber é o seguinte
@ Hawk sim, deveria. Não sei se esse é o comportamento desejado, mas seu código não envia o formulário, portanto o meu também não. Se você quiser enviá-lo, remova event.preventDefaultou faça algo parecido input.parents("form")[0].submit().
precisa saber é o seguinte
E se você enviar sem um clique (tecla Enter?), Será uma bagunça total .. #
FrancescoMM FrancescoMM
4

Como não posso comentar sobre a resposta aceita, trago aqui uma versão modificada que deve levar em conta elementos que estão fora do formulário (ou seja: anexados ao formulário usando o formatributo). Isso é para o navegador moderno: http://caniuse.com/#feat=form-attribute . O closest('form') é usado como um substituto para o formatributo não suportado

$(document).on('click', '[type=submit]', function() {
    var form = $(this).prop('form') || $(this).closest('form')[0];
    $(form.elements).filter('[type=submit]').removeAttr('clicked')
    $(this).attr('clicked', true);
});

$('form').on('submit', function() {
    var submitter = $(this.elements).filter('[clicked]');
})
user3074069
fonte
Essa realmente deve ser a resposta selecionada. Gostaria apenas de acrescentar que a regra deve ser"button,[type=submit]"
Wil
@Wil, mas nem todos os botões são botões de envio, depende do tipo.
Ptyskju 31/12/19
Sim, mas na ausência de um <input type = "submit">], todo UA que eu já usei tratou uma tag <button> como o gatilho de envio do formulário, portanto, esse será o caso. Ou o autor pode adicionar uma ressalva de que todos os elementos capazes de enviar devem ter propriedades [type = submit] ... um dos outros.
Wil
3
$("form input[type=submit]").click(function() {
    $("<input />")
        .attr('type', 'hidden')
        .attr('name', $(this).attr('name'))
        .attr('value', $(this).attr('value'))
    .appendTo(this)
});

adicionar campo oculto

anydasa
fonte
Não funciona no Firefox 30 / Windows 7. Nota: agora é necessário um campo oculto no firefox, pois ele pode não enviar o valor de envio clicado se enviado com jquery's: this.submit () em um evento de envio. Estou um pouco preocupado que $ (this) .attr ('name') possa não estar funcionando em todos os navegadores.
precisa saber é o seguinte
Eu tenho o Firefox 30 / Windows 7. Eu tenho tudo funcionando perfeitamente.
anydasa
Lembre-se de incluir button[type=submit]e input[type=image].
rybo111
3

Para mim, as melhores soluções foram estas:

$(form).submit(function(e){

   // Get the button that was clicked       
   var submit = $(this.id).context.activeElement;

   // You can get its name like this
   alert(submit.name)

   // You can get its attributes like this too
   alert($(submit).attr('class'))

});
Jeramiah Harland
fonte
3

Trabalhando com esta excelente resposta , você pode verificar o elemento ativo (o botão), anexar uma entrada oculta ao formulário e, opcionalmente, removê-la no final do manipulador de envio.

$('form.form-js').submit(function(event){
    var frm = $(this);
    var btn = $(document.activeElement);
    if(
        btn.length &&
        frm.has(btn) &&
        btn.is('button[type="submit"], input[type="submit"], input[type="image"]') &&
        btn.is('[name]')
    ){
        frm.append('<input type="hidden" id="form-js-temp" name="' + btn.attr('name') + '" value="' + btn.val() + '">');
    }

    // Handle the form submit here

    $('#form-js-temp').remove();
});

Nota lateral: eu adiciono pessoalmente a turma form-jsem todos os formulários enviados via JavaScript.

rybo111
fonte
2

Semelhante à resposta de Stan, mas:

  • se você tiver mais de um botão, precisará obter apenas o primeiro botão => [0]
  • se o formulário puder ser enviado com a tecla enter, você precisará gerenciar um padrão => myDefaultButtonId

$(document).on('submit', function(event) {
    event.preventDefault();
    var pressedButtonId = 
         typeof $(":input[type=submit]:focus")[0] === "undefined" ? 
         "myDefaultButtonId" :
         $(":input[type=submit]:focus")[0].id;
    ...
 }
yann-h
fonte
2

Esta é a solução usada por mim e funciona muito bem:

// prevent enter key on some elements to prevent to submit the form
function stopRKey(evt) {
  evt = (evt) ? evt : ((event) ? event : null);
  var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
  var alloved_enter_on_type = ['textarea'];
  if ((evt.keyCode == 13) && ((node.id == "") || ($.inArray(node.type, alloved_enter_on_type) < 0))) {
    return false;
  }
}

$(document).ready(function() {
  document.onkeypress = stopRKey;
  // catch the id of submit button and store-it to the form
  $("form").each(function() {
    var that = $(this);

    // define context and reference
    /* for each of the submit-inputs - in each of the forms on
			 the page - assign click and keypress event */
    $("input:submit,button", that).bind("click keypress", function(e) {
      // store the id of the submit-input on it's enclosing form
      that.data("callerid", this.id);
    });
  });

  $("#form1").submit(function(e) {
    var origin_id = $(e.target).data("callerid");
    alert(origin_id);
    e.preventDefault();

  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<form id="form1" name="form1" action="" method="post">
  <input type="text" name="text1" />
  <input type="submit" id="button1" value="Submit1" name="button1" />
  <button type="submit" id="button2" name="button2">
    Submit2
  </button>
  <input type="submit" id="button3" value="Submit3" name="button3" />
</form>

vasilenicusor
fonte
1

Isso me ajudou a https://stackoverflow.com/a/17805011/1029257

Formulário enviado somente após o clique no botão Enviar.

var theBtn = $(':focus');
if(theBtn.is(':submit'))
{
  // ....
  return true;
}

return false;
w4kskl
fonte
1

Aqui está a minha solução:

   $('#form').submit(function(e){   
        console.log($('#'+e.originalEvent.submitter.id));
        e.preventDefault();
    });
Roldan
fonte
0

Também fiz uma solução e funciona muito bem:
usa jQuery e CSS


Primeiro, criei uma classe CSS rápida, que pode ser incorporada ou em um arquivo separado.

<style type='text/css'>
    .Clicked {
        /*No Attributes*/
    }
</style>


Em seguida, no evento de clique de um botão no formulário, adicione a classe CSS ao botão. Se o botão já tiver a classe CSS, remova-o. (Não queremos duas classes CSS [apenas no caso]).

    // Adds a CSS Class to the Button That Has Been Clicked.
    $("form :input[type='submit']").click(function () 
    {
        if ($(this).hasClass("Clicked"))
        {
            $(this).removeClass("Clicked");
        }
        $(this).addClass("Clicked");
    });


Agora, teste o botão para ver se ele tem a classe CSS; se o botão testado não tiver o CSS, o outro botão terá.

    // On Form Submit
    $("form").submit(function ()
    {
        // Test Which Button Has the Class
        if ($("input[name='name1']").hasClass("Clicked"))
        {
            // Button 'name1' has been clicked.
        }
        else
        {
           // Button 'name2' has been clicked.
        }
    });

Espero que isto ajude! Felicidades!

Jason Cidras
fonte
Lembre-se de incluir button[type=submit]e input[type=image].
rybo111
Você deve remover as referências ao estilo e ao CSS - elas não são relevantes aqui. Além disso, as linhas em que você usa hasClass()e removeClass()são redundantes, pois addClass()não adicionarão a mesma classe duas vezes.
rybo111
0

Você pode criar o tipo de entrada = "oculto" como titular das informações de identificação do botão.

<input type="hidden" name="button" id="button">
<input type="submit" onClick="document.form_name.button.value = 1;" value="Do something" name="do_something">

Nesse caso, o formulário passa o valor "1" (id do seu botão) ao enviar. Isso funciona se onClick ocorrer antes de enviar (?), O que não tenho certeza se é sempre verdade.

darekk
fonte
0

Uma maneira simples de distinguir qual <button> ou <input type = "button" ...> é pressionado é verificando seu 'id':

$("button").click(function() {
  var id = $(this).attr('id');
  ... 
});
Apostolos
fonte
0

Aqui está uma amostra que usa this.form para obter o formulário correto no qual o envio está inserido e campos de dados para armazenar o último elemento clicado / focado. Também agrupei o código de envio dentro de um tempo limite para garantir que os eventos de clique aconteçam antes de serem executados (alguns usuários relataram nos comentários que no Chrome algumas vezes um evento de clique é acionado após o envio).

Funciona ao navegar com as teclas e com o mouse / dedos sem contar com os navegadores para enviar um evento de clique na tecla RETURN (embora não doa), adicionei um manipulador de eventos para eventos de foco de botões e campos.

Você pode adicionar botões do tipo = "enviar" aos itens que se salvam quando clicados.

Na demonstração, defino uma borda vermelha para mostrar o item selecionado e um alerta que mostra nome e valor / rótulo.

Aqui está o FIDDLE

E aqui está o (mesmo) código:

Javascript:

$("form").submit(function(e) {
  e.preventDefault();
  // Use this for rare/buggy cases when click event is sent after submit
  setTimeout(function() {

    var $this=$(this);
    var lastFocus = $this.data("lastFocus");
    var $defaultSubmit=null;

    if(lastFocus) $defaultSubmit=$(lastFocus);

    if(!$defaultSubmit || !$defaultSubmit.is("input[type=submit]")) {
      // If for some reason we don't have a submit, find one (the first)
      $defaultSubmit=$(this).find("input[type=submit]").first();
    }

    if($defaultSubmit) {
      var submitName=$defaultSubmit.attr("name");
      var submitLabel=$defaultSubmit.val();

       // Just a demo, set hilite and alert
      doSomethingWith($defaultSubmit);
      setTimeout(function() {alert("Submitted "+submitName+": '"+submitLabel+"'")},1000);
    } else {
      // There were no submit in the form
    }

  }.bind(this),0);

});

$("form input").focus(function() {
  $(this.form).data("lastFocus", this);
});
$("form input").click(function() {
  $(this.form).data("lastFocus", this);
});

// Just a demo, setting hilite
function doSomethingWith($aSelectedEl) {
  $aSelectedEl.css({"border":"4px solid red"});
  setTimeout(function() { $aSelectedEl.removeAttr("style"); },1000);
}

DUMMY HTML:

<form>
<input type="text" name="testtextortexttest" value="Whatever you write, sir."/>
<input type="text" name="moretesttextormoretexttest" value="Whatever you write, again, sir."/>

<input type="submit" name="test1" value="Action 1"/>
<input type="submit" name="test2" value="Action 2"/>
<input type="submit" name="test3" value="Action 3"/>
<input type="submit" name="test4" value="Action 4"/>
<input type="submit" name="test5" value="Action 5"/>
</form>

DUMB CSS:

input {display:block}
FrancescoMM
fonte
0

Eu escrevo esta função que me ajuda

var PupulateFormData= function (elem) {
var arr = {};
$(elem).find("input[name],select[name],button[name]:focus,input[type='submit']:focus").each(function () {
    arr[$(this).attr("name")] = $(this).val();
});
return arr;
};

e depois use

var data= PupulateFormData($("form"));
BabiBN
fonte
0
$('form').submit(function (ev) {
  let clickedButton = ev.originalEvent.explicitOriginalTarget;
});
Alexandre Paulo
fonte
-1

Você deseja usar window.event.srcElement.id assim:

function clickTheButton() {

var Sender = window.event.srcElement;
alert("the item clicked was " + Sender.id)

}

para um botão parecido com:

<input type="button" id="myButton" onclick="clickTheButton();" value="Click Me"/>

você receberá um alerta que diz: "o item clicado era myButton.

No seu exemplo aprimorado, você pode adicionar window.event.srcElement a process_form_submission e terá uma referência a qualquer elemento que invocou o processo.

Cos Callis
fonte
1
não usar onclick para elementos, ele é obsoleto
Krzysztof Cygan
1
Parece não funcionar para os botões de envio, window.event.srcElement está retornando o ID do formulário.
333Mhz
@KrzysztofCygan Você tem alguma fonte disso?
vard 25/09