¿Cómo puedo obtener el botón que provocó el envío desde el evento de envío de formulario?

110

Estoy tratando de encontrar el valor del botón enviar que activó el formulario para enviar

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

});

Posiblemente podría disparar un evento $ ("input [type = submit]"). Click () para cada botón y establecer alguna variable, pero eso parece menos elegante que cómo quitar el botón del formulario al enviar.

cazador
fuente
2
Tenga en cuenta que no existe necesariamente tal botón. Un guión podría servir formobj.submit(). Creo que los eventos de clic son el camino a seguir.
Jason Orendorff
1
Para los navegadores modernos, ahora existe el event.submitterque le da el <button>o en el <input type="submit">que se hizo clic.
gfv

Respuestas:

107

Aproveché document.activeElementcomo se muestra en esta respuesta: ¿Cómo obtener el elemento enfocado con jQuery?

$form.on('submit', function() {
    var $btn = $(document.activeElement);

    if (
        /* there is an activeElement at all */
        $btn.length &&

        /* it's a child of the form */ 
        $form.has($btn) &&

        /* it's really a submit element */
        $btn.is('button[type="submit"], input[type="submit"], input[type="image"]') &&

        /* it has a "name" attribute */
        $btn.is('[name]')
    ) {
        console.log("Seems, that this element was clicked:", $btn);
        /* access $btn.attr("name") and $btn.val() for data */
    }
});

Aprovecho el hecho de que el botón siempre es el elemento enfocado después de hacer clic en él. Esto no funcionará si lo hace blur()justo después del clic.

@Doin ha detectado otro inconveniente. Si un usuario envía el formulario a través enterde un campo de texto, document.activeElementno se establece. Debería tener cuidado con esto usted mismo, manejando keypresseventos en input[type="text"]y similares.

Actualización 2017-01: para mi biblioteca Hyperform , elegí no usar activeElementsino capturar todos los eventos que conducen al envío del formulario. El código para esto está en Github.

Si usa Hyperform, así es como accedería al botón que activó el envío:

$(form).on('submit', function(event) {
  var button = event.submittedVia;
});
Boldewyn
fuente
1
MDN dice que sí. (FF3, IE4, Chrome 2, Safari 4, Opera 9)
Boldewyn
1
Esta debería ser la respuesta.
Royi Namir
2
Hmmm ... ¿Funciona esto si el formulario se envía a través de [Enter]? Spec dice que debería ser equivalente a hacer clic en el botón de envío predeterminado, pero no estoy seguro de que lo deje como elemento activo o no.
Haciendo
8
Lamentablemente, no puede confiar solo enactiveElement , porque el "remitente" se puede definir: A. Mediante la activación de clic auténtico (se hace clic directamente con el mouse o el teclado Espacio / Intro); B. Mediante activación de clic sintético ( buttonElement.click()o buttonElement.dispatch(new MouseEvent(...)), sin enfocar); C. a través de envío de formulario implícitamente (interacción del teclado con el campo de otro formulario) D. a ninguno, cuandoformElement.submit()
mems
5
Solo una nota para mantener las cosas actualizadas. A partir de ahora, Safari establece activeElement en el cuerpo del documento al hacer clic en un botón de envío. Me encontré con este problema en Safari 9.1
coderkevin
35

Implementé esto y supongo que servirá.

$(document).ready(function() {
    $("form").submit(function() { 

    var val = $("input[type=submit][clicked=true]").val()

    // DO WORK

});

y este es el evento del botón de enviar que lo configura

$("form input[type=submit]").click(function() {
    $("input[type=submit]", $(this).parents("form")).removeAttr("clicked");
    $(this).attr("clicked", "true");
});

Gracias por las respuestas, pero esto no es tan poco elegante ...

cazador
fuente
1
No apto para navegadores cruzados / múltiples botones de envío.
user1610743
2
¿Está garantizado que el evento de clic se activa antes del evento de envío de formulario? Sugeriría darle al formulario una doNotSubmitclase, dentro de la función de envío verifique si la clase no existe (de lo contrario, no envíe), dentro del evento de clic elimine la clase de formulario doNotSubmity haga un $ (''. MyForm '). ('enviar'); después de agregar el valor del botón de envío como un campo oculto
Hafenkranich
2
@Hafenkranich En algunas raras ocasiones en Chrome, el evento de clic se activa DESPUÉS del evento de envío.
romanoza
@roamnoza eso es exactamente lo que encontré también.
Hafenkranich
2
@ j4k3 ¿Tienes una mejor solución? Esta respuesta es de 8 años
hunter
10

Creé un formulario de prueba y usando Firebug encontré esta forma de obtener el valor;

$('form').submit(function(event){
  alert(event.originalEvent.explicitOriginalTarget.value);
}); 

Desafortunadamente, solo Firefox admite este evento.

Dave Anderson
fuente
5
IE no tiene esta propiedad (explicitOriginalTarget)
andres descalzo
1
El viejo IE lo estropeó, pasó por Firebug y no pudo ver ningún otro valor de objeto para usar, pero simplemente se olvidó de los navegadores alternativos. Sin embargo, no me sorprende escuchar que no lo haga, tendrá que verificar Chrome.
Dave Anderson
11
Solo Firefox admite este evento.
Andy E
Entonces, básicamente, hay algo en HTML que no se puede hacer en JS ... Mozilla decidió implementar la función que faltaba, ¿y nadie hizo lo mismo? Por eso no podemos tener cosas bonitas
aross
6

Aquí hay un enfoque que parece más limpio para mis propósitos.

Primero, para todas y cada una de las formas:

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

Cuando se activa este evento de clic para un formulario, simplemente registra el objetivo de origen (disponible en el objeto de evento) para acceder más tarde. Este es un trazo bastante amplio, ya que se activará con 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 consultar en qué se hizo clic por última vez, con algo como

if ($(this).data('clicked').is('[name=no_ajax]')) xhr.abort();
Jonathan Camenisch
fuente
6

Ahora hay una submitterpropiedad estándar en el evento de envío.
¡Ya implementado en Firefox 75 !

document.addEventListener('submit',function(e){
    console.log(e.submitter)
})

Para los navegadores que no lo admiten, use este polyfill :

!function(){
    var lastBtn = null
    document.addEventListener('click',function(e){
        if (!e.target.closest) return;
        lastBtn = e.target.closest('button, input[type=submit]');
    }, true);
    document.addEventListener('submit',function(e){
        if ('submitter' in e) return;
        var canditates = [document.activeElement, lastBtn];
        for (var i=0; i < canditates.length; i++) {
            var candidate = canditates[i];
            if (!candidate) continue;
            if (!candidate.form) continue;
            if (!candidate.matches('button, input[type=button], input[type=image]')) continue;
            e.submitter = candidate;
            return;
        }
        e.submitter = e.target.querySelector('button, input[type=button], input[type=image]')
    }, true);
}();
Tobias Buschor
fuente
4

Según este enlace , el objeto Evento contiene un campo Event.target, que:

Devuelve una cadena que representa el objeto que inició el evento.

Acabo de crear una página para probar cuál es ese valor, y parece que esa representación es para el formulario en sí, no para el botón en el que se hizo clic. En otras palabras, Javascript no proporciona la posibilidad de determinar el botón en el que se hizo clic.

En cuanto a la solución de Dave Anderson , podría ser una buena idea probarla en varios navegadores antes de usarla. Es posible que funcione bien, pero no puedo decirlo de ninguna manera.

cmptrgeekken
fuente
1
El targetdevuelve todo el formulario. Por lo tanto, desafortunadamente, no es útil detectar el botón de envío correcto.
Hafenkranich
Lo digo en mi respuesta, @Hafenkranich.
cmptrgeekken
Event.targetno es una cadena. Es un EventTargetobjeto, que en este caso es el formulario enviado.
Akseli
4

Un enfoque limpio es utilizar el evento de clic en cada botón de formulario. A continuación se muestra un formulario html con botones para guardar, cancelar y eliminar:

<form  name="formname" action="/location/form_action" method="POST">
<input name="note_id" value="some value"/>
<input class="savenote" type="submit" value="Save"/>
<input class="cancelnote" type="submit" value="Cancel"/>
<input class="deletenote" type="submit" value="Delete" />
</form> 

A continuación se muestra el archivo jquery. Envío la 'acción' apropiada a la misma función del servidor según el botón en el que se hizo clic ('guardar' o 'eliminar'). Si se hace clic en 'cancelar', simplemente recargo la página.

$('.savenote').click(function(){
   var options = {
       data: {'action':'save'}
   };
   $(this).parent().ajaxSubmit(options);
   });


$('.deletenote').click(function(){
   var options = {
       data: {'action':'delete'}
   };
   $(this).parent().ajaxSubmit(options);
   });


$('.cancelnote').click(function(){
   window.location.reload(true);
   return false;
   });
usuario1128563
fuente
3

Busqué y encontré varias formas de enviar el botón de envío name+ valueal servidor usando jQuery + AJAX. No me gustaron mucho ...

¡Una de las mejores fue la solución de Hunter presentada aquí!

Pero yo mismo escribí otro.

Quiero compartir, porque es bueno y, según lo necesitaba, también funciona con formularios cargados a través de ajax (después de document.ready):

$(document).on('click', 'form input[type=submit]', function(){
    $('<input type="hidden" />').appendTo($(this).parents('form').first()).attr('name', $(this).attr('name')).attr('value', $(this).attr('value'));
});

¡Sencillo! Cuando se hace clic en el botón enviar, se agrega un campo oculto al formulario, utilizando el mismo namey valuedel botón enviar.

EDITAR: La versión a continuación es más fácil de leer. Además, se encarga de eliminar los campos ocultos previamente añadidos (en el caso de enviar el mismo formulario dos veces, lo cual es perfectamente posible cuando se usa AJAX).

Código mejorado:

$(document).on('click', 'form input[type=submit]', function(){
    var name   = $(this).attr('name');
    if (typeof name == 'undefined') return;
    var value  = $(this).attr('value');
    var $form  = $(this).parents('form').first();
    var $input = $('<input type="hidden" class="temp-hidden" />').attr('name', name).attr('value', value);
    $form.find('input.temp-hidden').remove();
    $form.append($input);
});
J. Bruni
fuente
1+ Esto lo hizo por mí. ¡Gracias!
Lennart Rolland
Parece que esto depende de que todos los <button/>s compartan el mismo nameatributo. Usaría una clase y me referiría a eso en su .remove().
binki
@binki: no, no es así. De todos modos, cada desarrollador puede adaptar la solución a sus propias necesidades.
J. Bruni
jsfiddle.net/binki/d8ecv muestra lo que quiero decir con respecto al nameatributo. Si el formulario se reutiliza y contiene entradas de envío con nombres diferentes, las entradas antiguas pueden saturar el formulario.
binki
Ahora veo ... Actualizaré mi respuesta. (De hecho, no presté mucha atención a su comentario debido al hecho no relacionado de que menciona el <button/>elemento, mientras que <input type="submit" />se usa en el código ...)
J. Bruni
2

Probé algunos de los ejemplos proporcionados, pero no funcionaron para nuestros propósitos.

Aquí hay un violín para mostrar: http://jsfiddle.net/7a8qhofo/1/

Me enfrenté a un problema similar, y así es como resolvimos el problema en nuestros formularios.

$(document).ready(function(){

    // Set a variable, we will fill later.
    var value = null;

    // On submit click, set the value
    $('input[type="submit"]').click(function(){
        value = $(this).val();
    });

    // On data-type submit click, set the value
    $('input[type="submit"][data-type]').click(function(){
        value = $(this).data('type');
    });

    // Use the set value in the submit function
    $('form').submit(function (event){
        event.preventDefault();
        alert(value);
        // do whatever you need to with the content
    });
});
Rudi Strydom
fuente
2

(evento)

function submForm(form,event){
             var submitButton;

             if(typeof event.explicitOriginalTarget != 'undefined'){  //
                 submitButton = event.explicitOriginalTarget;
             }else if(typeof document.activeElement.value != 'undefined'){  // IE
                 submitButton = document.activeElement;
             };

             alert(submitButton.name+' = '+submitButton.value)
}


<form action="" method="post" onSubmit="submForm(this, event); return false;">
user3126867
fuente
1

puede probar de esta manera con "event.originalEvent.x" y "event.originalEvent.y":

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> 
    <title>test</title>
</head>
<body>

    <form id="is_a_form">
        <input id="is_a_input_1" type="submit"><br />
        <input id="is_a_input_2" type="submit"><br />
        <input id="is_a_input_3" type="submit"><br />
        <input id="is_a_input_4" type="submit"><br />
        <input id="is_a_input_5" type="submit"><br />
    </form>

</body>
</html>
<script>
$(function(){

    $.fn.extend({
      inPosition: function(x, y) {

        return this.each(function() {

            try{
                var offset = $(this).offset();

                if ( (x >= offset.left) &&
                     (x <= (offset.left+$(this).width())) &&
                     (y >= offset.top) &&
                     (y <= (offset.top+$(this).height())) )
                {
                    $(this).css("background-color", "red");
                }
                else
                {
                        $(this).css("background-color", "#d4d0c8");
                }
                }
                catch(ex)
                {
                }

        });
      }
    }); 

    $("form").submit(function(ev) {

        $("input[type='submit']").inPosition(ev.originalEvent.x ,ev.originalEvent.y);
        return false;

    });

});
</script>
andres descalzo
fuente
"¡Ay!", funciona, no funciona, es útil, no es útil, ...? , No hablo bien inglés
andres descalzo
@hunter ¿cómo puedo votar a favor tu comentario varias veces?
Tom Auger
0

jQuery no parece proporcionar esos datos sobre el evento de envío. Parece que el método que propuso es su mejor opción.

Matchu
fuente
0

Solo otra solución, ya que ninguna otra cumplía mis requisitos. La ventaja es que se detectan los clics y las pulsaciones de teclas (enter y espacio) .

// Detects the Events
var $form = $('form');
$form.on('click keypress', 'button[type="submit"]', function (ev) {

    // Get the key (enter, space or mouse) which was pressed.
    if (ev.which === 13 || ev.which === 32 || ev.type === 'click') {

        // Get the clicked button
        var caller = ev.currentTarget;

        // Input Validation
        if (!($form.valid())) {
            return;
        }

        // Do whatever you want, e.g. ajax...
        ev.preventDefault();
        $.ajax({
            // ...
        })
    }
}

Esto funcionó mejor para mí.

Roland
fuente
0

Con un controlador de eventos más específico y JQuery, su objeto de evento es el botón en el que se hace clic. También puede obtener el formulario de delegación de este evento si es necesario.

$('form').on('click', 'button', function (e) {
  e.preventDefault();
  var
    $button = $(e.target),
    $form = $(e.delegateTarget);
  var buttonValue = $button.val();
});

Este documento tiene todo lo que necesita para comenzar. JQuery Doc .


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