Como posso obter o botão que causou o envio do evento de envio de formulário?

110

Estou tentando encontrar o valor do botão de envio que acionou o formulário de envio

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

});

Eu poderia disparar um evento $ ("input [type = submit]"). Click () para cada botão e definir alguma variável, mas isso parece menos elegante do que puxar o botão do formulário ao enviar.

caçador
fonte
2
Observe que não existe necessariamente esse botão. Um script pode servir formobj.submit(). Acho que os eventos de clique são o caminho a percorrer.
Jason Orendorff
1
Para navegadores modernos, agora existe o event.submitterque mostra o <button>ou <input type="submit">que foi clicado.
gfv

Respostas:

107

Eu aproveitei document.activeElementconforme esboçado nesta resposta: Como obter o elemento focado com 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 */
    }
});

Aproveito o fato de que o botão é sempre o elemento em foco depois de clicar nele. Isso não funcionará se você fizer um blur()clique logo após o clique.

@Doin identificou outra desvantagem. Se um usuário enviar o formulário por meio enterde um campo de texto, o document.activeElementnão será definido. Você precisa ficar atento a isso sozinho, ao lidar com keypresseventos em input[type="text"]e semelhantes.

Atualização 2017-01: Para minha biblioteca Hyperform , optei por não usar, activeElementmas capturar todos os eventos que levam ao envio do formulário. O código para isso está no Github.

Se acontecer de você usar o Hyperform, é assim que você acessaria o botão que acionou o envio:

$(form).on('submit', function(event) {
  var button = event.submittedVia;
});
Boldewyn
fonte
1
MDN diz que sim. (FF3, IE4, Chrome 2, Safari 4, Opera 9)
Boldewyn
1
Esta deve ser a resposta.
Royi Namir
2
Hmmm ... isso funciona se o formulário for adicionado via [Enter]? Spec diz que deve ser equivalente a clicar no botão enviar padrão, mas não tenho certeza se o deixa como o elemento ativo ou não.
Doin
8
Infelizmente, você não pode confiar apenas emactiveElement , porque o "solicitante" pode ser definido: A. Via ativação de clique autêntico (clicado diretamente com o mouse ou teclado Espaço / Enter); B. Via ativação de clique sintético ( buttonElement.click()ou buttonElement.dispatch(new MouseEvent(...)), sem alterar o foco); C. via submissão implícita do formulário (interação do teclado com o campo de outro formulário) D. para nenhum, quandoformElement.submit()
relembrar
5
Apenas uma nota para manter as coisas atualizadas. A partir de agora, o Safari define activeElement para o corpo do documento ao clicar em um botão de envio. Encontrei esse problema no Safari 9.1
coderkevin
35

Eu implementei isso e suponho que funcionará.

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

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

    // DO WORK

});

e este é o evento do botão de envio que o configura

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

Obrigado pelas respostas, mas isso não é terrivelmente deselegante ...

caçador
fonte
1
Não compatível com crossbrowser / múltiplos botões de envio.
user1610743
2
É garantido que o evento de clique seja acionado antes do evento de envio do formulário? Eu sugiro dar ao formulário uma doNotSubmitclasse, dentro da função submit, verifique se a classe não existe (caso contrário, não envie), dentro do evento click remova a classe do formulário doNotSubmite faça um $ (''. MyForm '). ('enviar'); após adicionar o valor do botão de envio como um campo oculto
Hafenkranich
2
@Hafenkranich Em algumas raras ocasiões no Chrome, o evento de clique é acionado APÓS o evento de envio.
romanoza
@roamnoza isso é exatamente o que eu encontrei também.
Hafenkranich
2
@ j4k3 Tem uma solução melhor? Esta resposta é 8 anos
hunter
10

Criei um formulário de teste e usando o Firebug encontrei essa forma de obter o valor;

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

Infelizmente, apenas o Firefox oferece suporte a este evento.

Dave Anderson
fonte
5
O IE não tem esta propriedade (explicitOriginalTarget)
andres descalzo
1
O bom e velho IE bagunçando tudo, passou pelo Firebug e não conseguiu ver nenhum outro valor de objeto para usar, mas esqueci completamente dos navegadores alternativos. Não estou surpreso ao saber que não, porém, terei que verificar o Chrome.
Dave Anderson
11
Apenas o Firefox oferece suporte a este evento.
Andy E
Então, basicamente, há algo em HTML que não pode ser feito em JS ... A Mozilla decidiu implementar o recurso que faltava e ninguém fez o mesmo? É por isso que não podemos ter coisas boas
volta de
6

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

Primeiro, para todo e qualquer formulário:

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

Quando este evento de clique é disparado para um formulário, ele simplesmente registra o destino de origem (disponível no objeto de evento) para ser acessado posteriormente. Este é um traço bastante amplo, já que disparará com qualquer clique em qualquer lugar do formulário. Comentários de otimização são bem-vindos, mas suspeito que isso nunca causará problemas perceptíveis.

Então, em $ ('form'). Submit (), você pode consultar o que foi clicado por último, com algo como

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

Agora existe uma submitterpropriedade padrão no evento de envio.
Já implementado no Firefox 75 !

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

Para navegadores que não o suportam, 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
fonte
4

De acordo com este link , o objeto Event contém um campo Event.target, que:

Retorna uma string que representa o objeto que iniciou o evento.

Acabei de criar uma página testando qual é esse valor e parece que essa representação é para o formulário em si, não para o botão clicado. Em outras palavras, o Javascript não fornece a facilidade de determinar o botão clicado.

No que diz respeito à solução de Dave Anderson , pode ser uma boa ideia testar isso em vários navegadores antes de usá-lo. É possível que funcione bem, mas não posso dizer de nenhuma maneira.

cmptrgeekken
fonte
1
O targetretorna o formulário completo. Portanto, infelizmente não é útil detectar o botão de envio correto.
Hafenkranich
Eu digo o mesmo em minha resposta, @Hafenkranich.
cmptrgeekken
Event.targetnão é uma string. É um EventTargetobjeto, que neste caso é o próprio formulário enviado.
Akseli
4

Uma abordagem limpa é usar o evento click em cada botão do formulário. A seguir está um formulário html com botões para salvar, cancelar e excluir:

<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 seguir está o jquery. Eu envio a 'ação' apropriada para a mesma função do servidor dependendo de qual botão foi clicado ('salvar' ou 'excluir'). Se 'cancelar' for clicado, eu simplesmente recarrego a 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;
   });
user1128563
fonte
3

Eu pesquisei e encontrei várias maneiras de obter o botão de envio name+ valueenviado para o servidor usando jQuery + AJAX. Não gostei muito deles ...

Um dos melhores foi a solução de hunter apresentada aqui!

Mas eu mesmo escrevi outro.

Quero compartilhar, porque é bom, e como precisei funciona também com forms carregados via ajax (após 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'));
});

Simples! Quando o botão enviar é clicado, um campo oculto é adicionado ao formulário, usando o mesmo nameevalue do botão enviar.

EDIT: A versão abaixo é mais fácil de ler. Além disso, ele se encarrega de remover campos ocultos anexados anteriormente (no caso de enviar o mesmo formulário duas vezes, o que é perfeitamente possível quando se usa AJAX).

Código aprimorado:

$(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
fonte
1+ Isso fez isso por mim. Obrigado!
Lennart Rolland
Parece que isso depende de todos os <button/>s compartilharem o mesmo nameatributo. Eu usaria uma classe e me referiria a ela na sua .remove().
binki
@binki: não, não importa. De qualquer forma, cada desenvolvedor pode adaptar a solução às suas necessidades.
J. Bruni
jsfiddle.net/binki/d8ecv mostra o que quero dizer com relação ao nameatributo. Se o formulário for reutilizado e contiver entradas de envio com nomes diferentes, as entradas antigas podem confundir o formulário.
binki
Agora entendo ... atualizarei minha resposta. (Na verdade, não prestei muita atenção ao seu comentário devido ao fato não relacionado de que você mencionou o <button/>elemento, enquanto ele <input type="submit" />é usado no código ...)
J. Bruni
2

Tentei alguns dos exemplos fornecidos, mas eles não funcionaram para nossos objetivos.

Aqui está um violino para mostrar: http://jsfiddle.net/7a8qhofo/1/

Eu me deparei com um problema semelhante e foi assim que resolvemos o problema em nossos formulários.

$(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
fonte
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
fonte
1

você pode tentar desta forma com "event.originalEvent.x" e "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
fonte
"caramba" ?, funciona, não funciona, é útil, não é útil, ...? , Eu não falo bem inglês
andres descalzo
@hunter, como faço para votar a favor de seu comentário várias vezes?
Tom Auger
0

jQuery não parece fornecer esses dados no evento de envio. Parece que o método que você propôs é sua melhor aposta.

Matchu
fonte
0

Apenas outra solução, já que nenhuma outra atendeu aos meus requisitos. A vantagem é que o clique e o pressionamento de tecla (enter e espaço) são detectados.

// 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({
            // ...
        })
    }
}

Isso funcionou melhor para mim.

Roland
fonte
0

Com um manipulador de eventos mais específico e JQuery, seu objeto de evento é o botão clicado. Você também pode obter o formulário de delegação deste evento, se necessário.

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

Este documento tem tudo que você precisa para começar. JQuery Doc .


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 então use

var data= PupulateFormData($("form"));
BabiBN
fonte