O manipulador de eventos do OnChange para o botão de opção (INPUT type = "radio") não funciona como um valor

191

Estou procurando uma solução generalizada para isso.

Considere 2 entradas do tipo de rádio com o mesmo nome. Quando enviado, o que é verificado determina o valor que é enviado com o formulário:

<input type="radio" name="myRadios" onchange="handleChange1();" value="1" />
<input type="radio" name="myRadios" onchange="handleChange2();" value="2" />

O evento de alteração não é acionado quando um botão de opção é desmarcado. Portanto, se o rádio com o valor = "1" já estiver selecionado e o usuário selecionar o segundo, handleChange1 () não será executado. Isso apresenta um problema (pelo menos para mim), pois não há nenhum evento em que eu possa pegar essa desmarcação.

O que eu gostaria é uma solução alternativa para o evento onchange para o valor do grupo de caixas de seleção ou, alternativamente, um evento oncheck que detecta não apenas quando um rádio é marcado, mas também quando está desmarcado.

Tenho certeza de que alguns de vocês já enfrentaram esse problema antes. Quais são algumas soluções alternativas (ou, idealmente, qual é a maneira correta de lidar com isso)? Eu só quero pegar o evento de mudança, acessar o rádio verificado anteriormente, bem como o rádio verificado recentemente.

O PS
onclick parece ser um evento melhor (entre navegadores) para indicar quando um rádio é verificado, mas ainda não resolve o problema não verificado.

Suponho que faça sentido por que a troca de um tipo de caixa de seleção funciona em um caso como esse, pois altera o valor que ele envia quando você a marca ou desmarca. Desejo que os botões de opção se comportem mais como a troca de um elemento SELECT, mas o que você pode fazer ...

Mateus
fonte

Respostas:

179

var rad = document.myForm.myRadios;
var prev = null;
for (var i = 0; i < rad.length; i++) {
    rad[i].addEventListener('change', function() {
        (prev) ? console.log(prev.value): null;
        if (this !== prev) {
            prev = this;
        }
        console.log(this.value)
    });
}
<form name="myForm">
  <input type="radio" name="myRadios"  value="1" />
  <input type="radio" name="myRadios"  value="2" />
</form>

Aqui está uma demonstração do JSFiddle: https://jsfiddle.net/crp6em1z/

Michal
fonte
4
Apenas uma observação do motivo pelo qual eu escolhi essa resposta correta: Os manipuladores de eventos de clique são configurados em cada um dos botões de opção com o atributo name myRadiospara ler a variável prevque contém o rádio atualmente selecionado. É feita uma comparação dentro de cada manipulador de cliques para decidir se o rádio clicado é o mesmo que o armazenado preve, se não, o rádio clicado atualmente é armazenado lá. Dentro do manipulador de clique, você tem acesso ao previamente selecionados: preveo rádio selecionado:this
Matthew
26
Seria melhor armazenar em cache a função fora do loop, para que todos os rádios compartilhem o mesmo manipulador de eventos (agora eles têm manipuladores idênticos, mas são funções diferentes). Ou talvez colocar todos os rádios no interior de uma delegação embalagem e uso evento
Oriol
1
@Oriol, ou alguém com conhecimento, você poderia dar um exemplo de (1) cache fora do loop ou (2) wrapper + delegação de eventos?
Matt Voda
14
onclick não funciona com a selecção do teclado, você pode usar rad[i].addEventListener('change', function(){ //your handler code })e que irá trabalhar para ambos mouse e teclado
Juanmi Rodriguez
117

Eu faria duas alterações:

<input type="radio" name="myRadios" onclick="handleClick(this);" value="1" />
<input type="radio" name="myRadios" onclick="handleClick(this);" value="2" />
  1. Use o onclickmanipulador em vez de onchange- você está alterando o "estado verificado" da entrada de rádio, não o value, para que não ocorra um evento de alteração.
  2. Use uma única função e passe thiscomo parâmetro, que facilitará a verificação de qual valor está selecionado no momento.

ETA: Juntamente com sua handleClick()função, você pode rastrear o valor original / antigo do rádio em uma variável com escopo de página. Isso é:

var currentValue = 0;
function handleClick(myRadio) {
    alert('Old value: ' + currentValue);
    alert('New value: ' + myRadio.value);
    currentValue = myRadio.value;
}

var currentValue = 0;
function handleClick(myRadio) {
    alert('Old value: ' + currentValue);
    alert('New value: ' + myRadio.value);
    currentValue = myRadio.value;
}
<input type="radio" name="myRadios" onclick="handleClick(this);" value="1" />
<input type="radio" name="myRadios" onclick="handleClick(this);" value="2" />

Nate Cook
fonte
Obrigado, embora isso não resolva a questão de ter que obter o rádio verificado anteriormente e você também esteja afirmando algo que outros usuários já mencionaram, que é usar "onclick" em vez de "onchange". Na verdade, eu sabia disso desde o início, mas queria chamar a atenção para o fato de que o onchange não funciona como se poderia esperar e gostaria de lidar melhor com isso.
Matthew
1
Você está usando JavaScript, certo? Então, por que não manter apenas uma variável que armazena o último botão de opção selecionado conhecido? Se você estiver usando um único manipulador, poderá verificar esse valor armazenado antes de substituí-lo pelo novo valor. 1
jmort253
13
@ Matthew: O onclickmanipulador é apenas nomeado mal - ele lida com o evento da seleção do botão de opção, se o botão é clicado, tocado ou ativado pressionando "enter" em um teclado. O onchangemanipulador não é apropriado neste caso, pois o valor da entrada não muda, apenas o atributo marcado / não verificado. Adicionei um exemplo de script para abordar o valor anterior.
Nate Cook
1
"onClick" não manipulará o evento se o usuário usar o teclado (tabulação + barra de espaço) para selecionar o rádio (e isso é prática comum em formulários grandes).
HoldOffHunger
+1. "onChange ()" não é um evento acionável no ReactJS e esta é a única solução. Fonte: github.com/facebook/react/issues/1471
HoldOffHunger
42

Como você pode ver neste exemplo: http://jsfiddle.net/UTwGS/

HTML:

<label><input type="radio" value="1" name="my-radio">Radio One</label>
<label><input type="radio" value="2" name="my-radio">Radio One</label>

jQuery:

$('input[type="radio"]').on('click change', function(e) {
    console.log(e.type);
});

os eventos clicke changesão disparados ao selecionar uma opção de botão de opção (pelo menos em alguns navegadores).

Devo também salientar que, no meu exemplo, o clickevento ainda é acionado quando você usa a guia e o teclado para selecionar uma opção.

Portanto, o que quero dizer é que, embora o changeevento tenha sido disparado em alguns navegadores, o clickevento deve fornecer a cobertura necessária.

Philip Walton
fonte
6
Sim, porque existe uma console.logdeclaração no meu código, que o IE7 não suporta. Você poderia alertá-lo no IE, se quisesse.
Philip Walton
Obrigado. No entanto, isso não aborda como obter a caixa de seleção selecionada anteriormente. Também obrigado, eu não sabia que console.log não funcionava no IE.
Matthew
Não dobrar-se sobre manipuladores de eventos - em navegadores que fogo tanto o clicke changemanipuladores de eventos, você vai acabar chamando o manipulador de duas vezes. Nesse caso, como o @Matthew se preocupa com o valor anterior, isso pode levar a consequências ruins.
Nate Cook
3
@NateCook você claramente perdeu o ponto do meu exemplo. Eu estava mostrando que sempre que o evento de alteração é acionado, o evento de clique também é acionado. Só porque eu estava usando os dois eventos no meu exemplo não significa que eu estava promovendo. Eu acho que seu voto negativo não foi merecido.
Philip Walton
Mas você o estava promovendo - sua resposta original dizia "Então, o que quero dizer é que, embora o evento de alteração seja acionado em alguns navegadores, o evento de clique deve fornecer a cobertura necessária como alternativa e você pode usar a alteração nos navegadores que apoiam isso. " clické o manipulador correto aqui e o único que você precisa. Sua revisão esclarece isso, então estou removendo meu voto negativo.
Nate Cook
10

Que tal usar o evento de mudança do Jquery?

$(function() {
    $('input:radio[name="myRadios"]').change(function() {
        if ($(this).val() == '1') {
            alert("You selected the first option and deselected the second one");
        } else {
            alert("You selected the second option and deselected the first one");
        }
    });
});

jsfiddle: http://jsfiddle.net/f8233x20/

Razan Paul
fonte
$(this).val()também pode ser substituído por javascript nativo e.currentTarget.value, em que e é o objeto de evento passado pela função de retorno de chamada.
Eric
6

Como você pode ver aqui: http://www.w3schools.com/jsref/event_onchange.asp O atributo onchange não é suportado por botões de opção.

A primeira pergunta SO vinculada por você fornece a resposta: use o onclickevento e verifique o estado do botão de opção dentro da função que ele aciona.

Constantin Groß
fonte
Isso é um pouco incompleto. Então, depois de criar uma função para lidar com todos os cliques no grupo, como você verificaria qual deles foi desmarcado? Além disso, estou procurando uma solução não-jquery.
Matthew Matthew
O botão de opção clicado será verificado, todos os outros do mesmo grupo serão desmarcados. Ou você pode percorrer todos os botões de opção desse grupo e verificar o status deles. Se você forneceu mais informações sobre o que deseja fazer com os botões de opção, seria mais fácil dizer como continuar.
Constantin Groß 12/01
2
onclicknão é a mesma semântica, pois você pode alterar a verificação de um botão de opção através da entrada do teclado, pois a seleção de formulário do teclado está ativada.
precisa saber é
O @gsnedders onclick parece funcionar via entrada de teclado na maioria dos navegadores - Firefox, IE, Safari, Chrome. É estranho que ele faça o que a troca deve fazer, mas faz. Estou curioso para saber como isso funciona em plataformas móveis.
Matthew
1
w3schools para 1 e não resolveu o problema inteiro para 2, que era como obter o rádio selecionado anteriormente. 2 era a parte mais importante da pergunta em minha mente.
Matthew
6

Não acho que exista outra maneira de armazenar o estado anterior. Aqui está a solução com o jQuery

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> 
<script type="text/javascript">
    var lastSelected;
    $(function () {
        //if you have any radio selected by default
        lastSelected = $('[name="myRadios"]:checked').val();
    });
    $(document).on('click', '[name="myRadios"]', function () {
        if (lastSelected != $(this).val() && typeof lastSelected != "undefined") {
            alert("radio box with value " + $('[name="myRadios"][value="' + lastSelected + '"]').val() + " was deselected");
        }
        lastSelected = $(this).val();
    });
</script>

<input type="radio" name="myRadios" value="1" />
<input type="radio" name="myRadios" value="2" />
<input type="radio" name="myRadios" value="3" />
<input type="radio" name="myRadios" value="4" />
<input type="radio" name="myRadios" value="5" />

Depois de pensar um pouco mais sobre isso, decidi me livrar da variável e adicionar / remover classe. Aqui está o que eu tenho: http://jsfiddle.net/BeQh3/2/

Alex Okrushko
fonte
1
Pensei em usar um jQuery e definitivamente há muito mais que poderia ser feito com ele. Você pode vincular alguns eventos personalizados para verificados e desmarcados e até mesmo procurar peculiaridades entre navegadores. Parece uma boa ideia para um plugin.
Matthew Matthew
Obrigado. Eu gosto do jQuery, mas não está disponível para mim neste caso.
Matthew
Como o jQuery não está disponível para você?
precisa saber é o seguinte
1
Se o jQuery não estivesse disponível para mim, eu encontraria um trabalho diferente.
Adrian Carr
5

Sim, não há evento de alteração para o botão de opção atualmente selecionado. Mas o problema é quando cada botão de opção é considerado um elemento separado. Em vez disso, um grupo de rádio deve ser considerado um único elemento comoselect . Portanto, o evento de mudança é acionado para esse grupo. Se é umselect elemento, nunca nos preocupamos com cada opção, mas apenas com a opção selecionada. Armazenamos o valor atual em uma variável que se tornará o valor anterior, quando uma nova opção for selecionada. Da mesma forma, você deve usar uma variável separada para armazenar o valor do botão de opção marcado.

Se você deseja identificar o botão de opção anterior, é necessário ligar mousedown evento.

var radios = document.getElementsByName("myRadios");
var val;
for(var i = 0; i < radios.length; i++){
    if(radios[i].checked){
        val = radios[i].value;
    }
}

veja isto: http://jsfiddle.net/diode/tywx6/2/

Diodo
fonte
Pensei em usar o mouse para verificar antes que o clique acontecesse, mas isso não funcionará ao usar o espaço para selecionar o rádio. Talvez o evento de foco no lugar ou além do que você mencionou.
Matthew
5

Armazene o rádio verificado anteriormente em uma variável:
http://jsfiddle.net/dsbonev/C5S4B/

HTML

<input type="radio" name="myRadios" value="1" /> 1
<input type="radio" name="myRadios" value="2" /> 2
<input type="radio" name="myRadios" value="3" /> 3
<input type="radio" name="myRadios" value="4" /> 4
<input type="radio" name="myRadios" value="5" /> 5

JS

var changeHandler = (function initChangeHandler() {
    var previousCheckedRadio = null;

    var result = function (event) {
        var currentCheckedRadio = event.target;
        var name = currentCheckedRadio.name;

        if (name !== 'myRadios') return;

        //using radio elements previousCheckedRadio and currentCheckedRadio

        //storing radio element for using in future 'change' event handler
        previousCheckedRadio = currentCheckedRadio;
    };

    return result;
})();

document.addEventListener('change', changeHandler, false);

CÓDIGO EXEMPLO JS

var changeHandler = (function initChangeHandler() {
    var previousCheckedRadio = null;

    function logInfo(info) {
        if (!console || !console.log) return;

        console.log(info);
    }

    function logPrevious(element) {
        if (!element) return;

        var message = element.value + ' was unchecked';

        logInfo(message);
    }

    function logCurrent(element) {
        if (!element) return;

        var message = element.value + ' is checked';

        logInfo(message);
    }

    var result = function (event) {
        var currentCheckedRadio = event.target;
        var name = currentCheckedRadio.name;

        if (name !== 'myRadios') return;

        logPrevious(previousCheckedRadio);
        logCurrent(currentCheckedRadio);

        previousCheckedRadio = currentCheckedRadio;
    };

    return result;
})();

document.addEventListener('change', changeHandler, false);
Dimitar Bonev
fonte
Obrigado. Eu poderia levar o violino ainda mais longe e armazenar o próprio rádio. Dessa forma, quando o valor muda, você tem uma referência ao rádio selecionado anteriormente e não apenas ao seu valor.
Matthew
@ Matthew Na verdade, o violino que compartilhei estava armazenando o elemento rádio e não apenas o valor. O valor foi registrado para mostrar a finalidade do código, mas não foi armazenado para uso posterior. Estou editando os nomes das variáveis ​​para ficar mais esclarecido: previousChecked -> previousCheckedRadio, that -> currentCheckedRadio;
precisa saber é o seguinte
Obrigado. Esta é uma boa solução, exceto pelo uso do evento de alteração que não se comporta adequadamente entre navegadores. Se você usasse clicar, seria melhor.
Matthew
5

Sei que esse é um problema antigo, mas esse trecho de código funciona para mim. Talvez alguém no futuro considere útil:

<h2>Testing radio functionality</h2>
<script type="text/javascript">var radioArray=[null];</script>
<input name="juju" value="button1" type="radio" onclick="radioChange('juju','button1',radioArray);" />Button 1
<input name="juju" value="button2" type="radio" onclick="radioChange('juju','button2',radioArray);" />Button 2
<input name="juju" value="button3" type="radio" onclick="radioChange('juju','button3',radioArray);" />Button 3
<br />

<script type="text/javascript">
function radioChange(radioSet,radioButton,radioArray)
  {
  //if(radioArray instanceof Array) {alert('Array Passed');}
  var oldButton=radioArray[0];
  if(radioArray[0] == null)
    {
    alert('Old button was not defined');
    radioArray[0]=radioButton;
    }
  else
    {
    alert('Old button was set to ' + oldButton);
    radioArray[0]=radioButton;
    }
  alert('New button is set to ' + radioArray[0]);
  }
</script>
John K
fonte
1
Se você adicionar, onkeydown="radioChange('juju','button1',radioArray);"também poderá capturar quando um usuário usar espaço para verificar o rádio.
Matthew
2

Isso está no topo da minha cabeça, mas você pode fazer um evento onClick para cada botão de opção, fornecer todos os IDs diferentes e criar um loop for no evento para percorrer cada botão de opção do grupo e descobrir qual é foi verificado olhando o atributo 'marcado'. O ID do verificado seria armazenado como uma variável, mas convém usar uma variável temporária primeiro para garantir que o valor dessa variável seja alterado, pois o evento click seria acionado se um novo botão de opção fosse verificado ou não.

Andrew Latham
fonte
2
<input type="radio" name="brd" onclick="javascript:brd();" value="IN">   
<input type="radio" name="brd" onclick="javascript:brd();" value="EX">` 
<script type="text/javascript">
  function brd() {alert($('[name="brd"]:checked').val());}
</script>
umesh unnikrishnan
fonte
2

Se você deseja evitar o script embutido, pode simplesmente ouvir um evento de clique no rádio. Isso pode ser conseguido com Javascript simples ouvindo um evento de clique em

for (var radioCounter = 0 ; radioCounter < document.getElementsByName('myRadios').length; radioCounter++) {
      document.getElementsByName('myRadios')[radioCounter].onclick = function() {
        //VALUE OF THE CLICKED RADIO ELEMENT
        console.log('this : ',this.value);
      }
}
mareks
fonte
2

Você pode adicionar o seguinte script JS

<script>
    function myfunction(event) {
        alert('Checked radio with ID = ' + event.target.id);
    }
    document.querySelectorAll("input[name='gun']").forEach((input) => {
        input.addEventListener('change', myfunction);
    });
</script>
Zumbido
fonte
1

isso funciona para mim

<input ID="TIPO_INST-0" Name="TIPO_INST" Type="Radio" value="UNAM" onchange="convenio_unam();">UNAM

<script type="text/javascript">
            function convenio_unam(){
                if(this.document.getElementById('TIPO_INST-0').checked){
                    $("#convenio_unam").hide();
                }else{
                    $("#convenio_unam").show(); 
                }                               
            }
</script>
Doberon
fonte
Penso que esta deve ser a resposta agora que é suportada.
Leo
0

Essa é a função mais fácil e eficiente a ser usada, basta adicionar quantos botões você desejar ao selected = false e fazer com que o evento onclick de cada botão de opção chame essa função. Designe um número exclusivo para cada botão de opção

function AdjustRadios(which) 
{
    if(which==1)
         document.getElementById("rdpPrivate").checked=false;
    else if(which==2)
         document.getElementById("rdbPublic").checked=false;
}
Jama
fonte
0

Maneira mais fácil e potente

leia apenas entradas de rádio usando getAttribute

document.addEventListener('input',(e)=>{

if(e.target.getAttribute('name')=="myRadios")
console.log(e.target.value)
})
<input type="text"  value="iam text" /> 
<input type="radio" name="myRadios" value="1" /> 1
<input type="radio" name="myRadios" value="2" /> 2

ßãlãjî
fonte