jQuery: ¿cómo obtener en qué botón se hizo clic en el envío del formulario?

227

tengo un .submit() evento configurado para enviar formularios. También tengo varios formularios en la página, pero solo uno aquí para este ejemplo. Me gustaría saber en qué botón de envío se hizo clic sin aplicar un .click()evento a cada uno.

Aquí está la configuración:

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

Ejemplo en vivo en jsfiddle

Además de aplicar un evento .click () en cada botón, ¿hay alguna forma de determinar en qué botón de envío se hizo clic?

hawkexp
fuente
2
La ironía es, por supuesto, que esta información es trivial para determinar el lado del servidor.
Neil
55
@Neil No si está enviando el formulario a través de $ .ajax () y un serializeArray () en el formulario.
Aaron
1
¿Qué tal poner un oyente en los botones de envío y enviar el formulario manualmente?
JMRC
1
¿Qué tal mirar la versión serializada de los datos del formulario y hacer coincidir los botones name?
Augustin Riedinger

Respuestas:

235

Hice esta misma pregunta: ¿Cómo puedo obtener el botón que provocó el envío desde el evento de envío de formulario?

Terminé ideando esta solución y funcionó bastante bien:

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

En su caso con múltiples formularios, es posible que deba modificar esto un poco, pero aún debería aplicarse

cazador
fuente
11
+1 Buena solución. Es posible que desee agregar una declaración que restablezca el atributo cliqueado a falso en los botones en caso de que el envío del formulario se maneje de manera ajax y desee evitar que se haga clic nuevamente en el botón previsualmente.
Chandu
1
Oh ya veo. Estás agregando tu propio atributo "clic". Estaba buscando un booleano "cliqueado" y no pude encontrar uno en ninguna parte. Nunca pensé en hacer uno yo mismo. ¡Buena idea!
hawkexp
99
Tenga en cuenta que esto solo funciona con elementos de entrada, no con elementos de botón.
Bob.at.Indigo.Health
1
Esto es innecesario en Chrome con elementos de botón. Simplemente manejo el evento de envío, construyo un objeto FormData a partir del formulario, y siempre incluye el valor del botón en el que se hizo clic. Firefox no exhibe este comportamiento y, en cambio, no envía el valor del botón. Chrome parece estar haciendo algo como la solución anterior automáticamente, recordando en qué botón se hizo clic e incluyéndolo en los datos del formulario automáticamente. Por supuesto, esta falta de comportamiento estándar (plagar todo en JavaScript de todos modos, así que no me sorprende), hace que la característica sea inútil.
Triynko
1
Además, considere agregar el selector "button [type = submit]" (separado por comas) ya que los elementos de envío no tienen que ser etiquetas de entrada.
Kyselejsyreček
91

Encontré que esto funcionó.

$(document).ready(function() {
    $( "form" ).submit(function () {
        // Get the submit button element
        var btn = $(this).find("input[type=submit]:focus" );
    });
}
Stan
fuente
12
esta solución funciona bien en Chrome, pero no puede funcionar en Safari, devolverá indefinido, no el objeto del botón
Sr. Sun Lin
1
@Krinkle Lo codifiqué así para mostrar la forma más genérica posible. Creo que los desarrolladores son lo suficientemente inteligentes como para optimizarlo a su gusto.
Stan
Más limpio que las otras soluciones. Todavía no han encontrado una solución que funciona al presionar entran embargo
andrewtweber
Esta respuesta no funcionará siempre. En realidad, necesita el botón de clic, no el foco (ya que la acción de enfoque no es una acción de clic). La respuesta seleccionada (de cazador) es la correcta.
Vasil Popov
Tengo un evento de clic en los botones de enviar y enviar eventos en sus formularios ... este último activa el primero. Usé esto para averiguar la fuente del evento. En algunos navegadores, al hacer clic en el botón, obtendrá el botón enfocado, en otros no obtendrá nada. En todos los navegadores que probé, presionar enter mientras estaba en una entrada mantiene la entrada enfocada.
notacouch
65

Esto funciona para mi:

$("form").submit(function() {
   // Print the value of the button that was clicked
   console.log($(document.activeElement).val());
}
seddonym
fuente
3
Esto fue útil, para obtener la idde lo activeElementque hice$(document.activeElement).attr('id');
Mikolaj
99
Para lo que vale, no necesita usar jQuery para obtener la identificación: es aún más simple usar Javascript puro:document.activeElement.id
Nick F
55
Tenga en cuenta que esto no parece funcionar como se esperaba en Safari. :(
rinogo
@rinogo: ¿qué pasa en Safari?
Crescent Fresh
1
En Safari document.activeElementno es la entrada. En mi caso, es un div modal (que contiene el formulario).
mgalgs
46

Cuando se envía el formulario:

  • document.activeElement le dará el botón de enviar en el que se hizo clic.

  • document.activeElement.getAttribute('value') le dará el valor de ese botón.

Nick F
fuente
2
Tenga en cuenta que esto no parece funcionar como se esperaba en Safari. :(
rinogo
No sé qué tal Safari pero en Chrome funcionó bien :) ¡Gracias!
Ingus
Funciona en Chrome 63 e IE11
Sir Crispalot
Tenía una validación discreta de jquery ejecutándose, y eso intercepta antes de esto y establece la entrada no válida como activeElement en lugar del botón.
Peheje
28

Aquí está el enfoque que parece más limpio para mis propósitos.

Primero, para cualquiera y todas las formas:

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

Cuando se activa este evento de clic para un formulario, simplemente registra el destino de origen (disponible en el objeto de evento) para acceder más tarde. Este es un trazo bastante amplio, ya que se disparará por cualquier clic en cualquier parte del formulario. Los comentarios de optimización son bienvenidos, pero sospecho que nunca causará problemas notables.

Luego, en $('form').submit(), puede preguntar en qué se hizo clic por última vez, con algo como

if ($(this).data('clicked').is('[name=no_ajax]')) xhr.abort();
Jonathan Camenisch
fuente
¿Qué sucede si este es un evento de envío de formulario en lugar de clic?
Tony
1
@ Tony, lo siento, no vi tu pregunta antes. Esta respuesta se refiere completamente a los envíos de formularios. También se hace clic en los botones Enviar y, por lo tanto, se activa el evento de clic.
Jonathan Camenisch
2
Votar esta respuesta por encima de la respuesta del cazador ya que los eventos de envío del formulario se activan antes de cualquier evento secundario de clic de elemento. Esto causa dificultades cuando se cancela el evento de envío de su formulario return false, mientras que este método funciona correctamente ya que el clic está vinculado al formulario en lugar de a sus elementos secundarios.
pospi
77
Esto no puede manejar adecuadamente el caso en el que el formulario se envía por un medio que no sea un clic (por ejemplo, presionar Entrar). Cuando el formulario se envía por otros medios, es el primer botón de envío que activó el evento de envío, no el último en hacer clic.
quietmint
Falta un punto y coma después, ... $(event.target))pero no se puede editar porque las ediciones deben ser> 6 caracteres
Hafenkranich
13

¡Guau, algunas soluciones pueden complicarse! Si no le importa usar un simple global, solo aproveche el hecho de que el evento de clic del botón de entrada se dispara primero. Uno podría filtrar aún más el selector $ ('input') para uno de los muchos formularios utilizando $ ('# 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);
      });
    });
jugador de cesped
fuente
12

He encontrado que la mejor solución es

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

Esto no solo funciona en las entradas, sino que también funciona en las etiquetas de los botones. También obtiene la identificación del botón.

Thomas Williams
fuente
Maldita sea, tengo que ver si mi página funciona en Safari ahora doh. ¿Por qué Safari siempre tiene que ser tan difícil? Gracias por eso. Voy a revisar cuando llegue a casa
Thomas Williams
Esto no funciona para mí en Safari. Haga clic en oyente en un botón en el cuerpo llama .submit en un formulario. Dentro del controlador de envío $(document.activeElement).attr('id')regresaundefined
dyodji
7

Otra posible solución es agregar un campo oculto en su formulario:

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

Luego, en la función listo, agregue funciones para registrar qué tecla se presionó:

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

Ahora, en el controlador de envío de formularios, lea la variable oculta y decida en función de ella:

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

Sobre la base de lo que hicieron Stan y Yann-h, pero este predeterminado es el primer botón. La belleza de este enfoque general es que recoge tanto el clic como la tecla Intro (incluso si el foco no estaba en el botón. Si necesita permitir ingresar en el formulario, simplemente responda a esto cuando un botón esté enfocado ( es decir, la respuesta de Stan.) En mi caso, quería permitir que ingrese para enviar el formulario, incluso si el foco actual del usuario estaba en el cuadro de texto.

También estaba usando un atributo 'nombre' en lugar de 'id', pero este es el mismo enfoque.

var pressedButtonName =
     typeof $(":input[type=submit]:focus")[0] === "undefined" ?
     $(":input[type=submit]:first")[0].name :
     $(":input[type=submit]:focus")[0].name;
andrewmo
fuente
Por lo tanto, si mueve el foco al segundo botón con TAB y presiona RETORNO, el valor predeterminado será el primer botón ...
FrancescoMM
5

Este me funcionó

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

}
Saheb Mondal
fuente
¡Esto merece una medalla!
Martin Zvarík
4

Si lo que quiere decir al no agregar un evento .click es que no desea tener controladores separados para esos eventos, puede manejar todos los clics (envíos) en una función:

$(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
fuente
no es una mala idea. ¿Funcionaría esto para cualquier número de formularios en la página?
hawkexp
@ halcón sí, debería. No sé si este es el comportamiento deseado, pero su código en realidad no envía el formulario, por lo que el mío tampoco. Si desea enviarlo, simplemente elimine event.preventDefaulto haga algo como input.parents("form")[0].submit().
jcane86
Y si envía sin un clic (¿tecla de retorno?) Será un desastre total ..
FrancescoMM
4

Como no puedo comentar sobre la respuesta aceptada, traigo aquí una versión modificada que debe tener en cuenta los elementos que están fuera del formulario (es decir, adjuntos al formulario usando el formatributo). Esto es para el navegador moderno: http://caniuse.com/#feat=form-attribute . El closest('form') se utiliza como reserva para un formatributo no compatible

$(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]');
})
usuario3074069
fuente
Esta realmente debería ser la respuesta seleccionada. Solo agregaría que la regla debería ser"button,[type=submit]"
Wil
@Wil pero no todos los botones son botones de envío, depende de su tipo.
ptyskju
Sí, pero en ausencia de un <input type = "submit">], cada UA que he usado ha tratado una etiqueta <button> como el desencadenador de envío de formulario, por lo que esto detectará ese caso. O el autor puede agregar una advertencia de que todos los elementos con capacidad de envío deben tener propiedades [type = submit] ... uno del otro.
Wil
3
$("form input[type=submit]").click(function() {
    $("<input />")
        .attr('type', 'hidden')
        .attr('name', $(this).attr('name'))
        .attr('value', $(this).attr('value'))
    .appendTo(this)
});

agregar campo oculto

anydasa
fuente
No funciona en Firefox 30 / Windows 7. Nota: ahora se requiere un campo oculto en Firefox, ya que podría no enviar el valor de envío clicado si se envía con jquery's: this.submit () en un evento de envío. Me preocupa un poco que $ (this) .attr ('name') no funcione en todos los navegadores.
user1610743
Tengo Firefox 30 / Windows 7. Tengo todo funcionando perfectamente.
anydasa
Recuerde incluir button[type=submit]y input[type=image].
rybo111
3

Para mí, la mejor solución fue esta:

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

Al trabajar con esta excelente respuesta , puede verificar el elemento activo (el botón), agregar una entrada oculta al formulario y, opcionalmente, eliminarlo al final del controlador de envío.

$('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 al margen: personalmente agrego la clase form-jsen todos los formularios que se envían a través de JavaScript.

rybo111
fuente
2

Similar a la respuesta de Stan pero:

  • Si tiene más de un botón, debe obtener solo el primer botón => [0]
  • Si el formulario se puede enviar con la tecla Intro, debe administrar un valor predeterminado => 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
fuente
2

Esta es la solución utilizada por mí y funciona muy bien:

// 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
fuente
1

Aquí está mi solución:

   $('#form').submit(function(e){   
        console.log($('#'+e.originalEvent.submitter.id));
        e.preventDefault();
    });
Roldán
fuente
0

También hice una solución, y funciona bastante bien:
utiliza jQuery y CSS


Primero, hice una clase CSS rápida, esta puede ser incrustada o en un archivo separado.

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


A continuación, en el evento de clic de un botón dentro del formulario, agregue la clase CSS al botón. Si el botón ya tiene la clase CSS, elimínelo. (No queremos dos clases CSS [Por si acaso]).

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


Ahora, pruebe el botón para ver que tiene la clase CSS, si el botón probado no tiene el CSS, entonces el otro botón sí.

    // 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 esto ayude! ¡Salud!

Jason Cidras
fuente
Recuerde incluir button[type=submit]y input[type=image].
rybo111
Debe eliminar las referencias al estilo y CSS; no son relevantes aquí. Además, las líneas donde usa hasClass()y removeClass()son redundantes, ya addClass()que no agregarán la misma clase dos veces.
rybo111
0

Puede crear input type = "hidden" como titular de la información de identificación de un botón.

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

En este caso, el formulario pasa el valor "1" (identificación de su botón) al enviar. Esto funciona si onClick ocurre antes de enviar (?), Lo que no estoy seguro si siempre es cierto.

Darekk
fuente
0

Una forma simple de distinguir qué <botón> o <input type = "button" ...> se presiona, verificando su 'id':

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

Aquí hay una muestra que usa this.form para obtener la forma correcta en la que se envía el envío, y campos de datos para almacenar el último elemento seleccionado / enfocado. También envolví el código de envío dentro de un tiempo de espera para asegurarme de que los eventos de clic suceden antes de que se ejecute (algunos usuarios informaron en los comentarios que en Chrome a veces se dispara un evento de clic después de un envío).

Funciona cuando se navega tanto con las teclas como con el mouse / dedos sin contar con los navegadores para enviar un evento de clic en la tecla RETURN (aunque no duele), agregué un controlador de eventos para eventos de enfoque para botones y campos.

Puede agregar botones de tipo = "enviar" a los elementos que se guardan al hacer clic.

En la demostración, configuré un borde rojo para mostrar el elemento seleccionado y una alerta que muestra el nombre y el valor / etiqueta.

Aquí está el violín

Y aquí está el (mismo) 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);
}

HTML DUMMY:

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

CSS DUMB:

input {display:block}
FrancescoMM
fuente
0

Escribo esta función que me ayuda

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

y luego usar

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

Desea usar window.event.srcElement.id de esta manera:

function clickTheButton() {

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

}

para un botón que se parece a:

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

recibirá una alerta que dice: "el elemento en el que se hizo clic fue myButton.

En su ejemplo mejorado, puede agregar window.event.srcElement a process_form_submission y tendrá una referencia a cualquier elemento invocado en el proceso.

Cos Callis
fuente
1
no use onclick para elementos, está en desuso
Krzysztof Cygan
1
Esto no parece funcionar para los botones de envío, window.event.srcElement está devolviendo la identificación del formulario.
333Mhz
@KrzysztofCygan ¿Tienes alguna fuente de eso?
vard