El controlador de eventos OnChange para el botón de opción (INPUT type = "radio") no funciona como un valor

191

Estoy buscando una solución generalizada para esto.

Considere 2 entradas de tipo de radio con el mismo nombre. Cuando se envía, el que está marcado determina el valor que se envía con el formulario:

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

El evento de cambio no se activa cuando se desactiva un botón de radio. Entonces, si la radio con valor = "1" ya está seleccionada y el usuario selecciona la segunda, handleChange1 () no se ejecuta. Esto presenta un problema (para mí de todos modos) en que no hay ningún evento en el que pueda detectar esta des-selección.

Lo que me gustaría es una solución alternativa para el evento onchange para el valor del grupo de casillas de verificación o, alternativamente, un evento oncheck que detecte no solo cuando una radio está marcada sino también cuando no está marcada.

Estoy seguro de que algunos de ustedes se han encontrado con este problema antes. ¿Cuáles son algunas soluciones alternativas (o idealmente, cuál es la forma correcta de manejar esto)? Solo quiero ver el evento de cambio, acceder a la radio previamente verificada, así como a la radio recién verificada.

PS
onclick parece un mejor evento (cross-browser) para indicar cuándo se verifica una radio, pero aún así no resuelve el problema no verificado.

Supongo que tiene sentido por qué onchange para un tipo de casilla de verificación funciona en un caso como este, ya que cambia el valor que envía cuando lo marca o desmarca. Desearía que los botones de radio se comportaran más como el cambio de un elemento SELECT, pero qué puedes hacer ...

Mateo
fuente

Respuestas:

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>

Aquí hay una demostración de JSFiddle: https://jsfiddle.net/crp6em1z/

Michal
fuente
44
Solo una nota sobre por qué elegí esto como la respuesta correcta: los controladores de eventos Click están configurados en cada uno de los botones de radio con el atributo de nombre myRadiospara leer la variable prevque contiene la radio seleccionada actualmente. Se realiza una comparación dentro de cada controlador de clics para decidir si la radio clicada es la misma que la almacenada prevy, de lo contrario, la radio clicada actualmente se almacena allí. Dentro del controlador de clics, tiene acceso a la prevradio seleccionada previamente: y la radio seleccionada actualmente:this
Matthew
26
Sería mejor almacenar en caché la función fuera del bucle, de modo que todas las radios compartan el mismo controlador de eventos (ahora tienen controladores idénticos pero son funciones diferentes). O quizás coloque todas las radios dentro de un contenedor y use la delegación de eventos
Oriol
1
@Oriol, o cualquier persona con conocimientos, ¿podría dar un ejemplo de (1) almacenamiento en caché fuera del bucle o (2) envoltura + delegación de eventos?
Matt Voda
14
onclick no funciona con la selección de teclado, puede usarlo rad[i].addEventListener('change', function(){ //your handler code })y funcionará tanto para mouse como para teclado
Juanmi Rodriguez
117

Haría dos cambios:

<input type="radio" name="myRadios" onclick="handleClick(this);" value="1" />
<input type="radio" name="myRadios" onclick="handleClick(this);" value="2" />
  1. Use el onclickcontrolador en lugar de onchange: está cambiando el "estado verificado" de la entrada de radio, no el value, por lo que no está ocurriendo un evento de cambio.
  2. Use una sola función y pase thiscomo parámetro, lo que facilitará verificar qué valor está seleccionado actualmente.

ETA: junto con su handleClick()función, puede rastrear el valor original / antiguo de la radio en una variable de ámbito de página. Es decir:

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
fuente
Gracias, aunque esto no soluciona el problema de tener que obtener la radio marcada anteriormente y también está diciendo algo que otros usuarios ya han mencionado que es usar "onclick" en lugar de "onchange". En realidad lo sabía desde el principio, pero quería llamar la atención sobre el hecho de que onchange no funciona como cabría esperar y me gustaría manejarlo mejor.
Mateo
1
Estás usando JavaScript, ¿verdad? Entonces, ¿por qué no simplemente mantener una variable que almacena el último botón de radio seleccionado conocido? Si está utilizando un único controlador, puede verificar este valor almacenado antes de sobrescribirlo con el nuevo valor. +1
jmort253
13
@Matthew: el onclickcontrolador tiene un nombre incorrecto: maneja el evento de selección del botón de opción si se hace clic, se toca o se activa el botón presionando "enter" en un teclado. El onchangecontrolador no es apropiado en este caso ya que el valor de la entrada no cambia, solo el atributo marcado / no marcado. He agregado un script de ejemplo para abordar el valor anterior.
Nate Cook
1
"onClick" no manejará el evento si el usuario usó el teclado (pestaña + barra espaciadora) para seleccionar la radio (y esta es una práctica común en formularios grandes).
HoldOffHunger
+1. "onChange ()" no es un evento disparable en ReactJS y esta es la única solución. Fuente: github.com/facebook/react/issues/1471
HoldOffHunger
42

Como puede ver en este ejemplo: 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);
});

tanto el clicky changeeventos se activan al seleccionar un botón de opción (por lo menos en algunos navegadores).

También debo señalar que, en mi ejemplo, el clickevento aún se dispara cuando usa la pestaña y el teclado para seleccionar una opción.

Entonces, mi punto es que, aunque el changeevento se dispara en algunos navegadores, el clickevento debe proporcionar la cobertura que necesita.

Philip Walton
fuente
66
Sí, porque hay una console.logdeclaración en mi código que IE7 no admite. Podrías alertarlo en IE si quisieras.
Philip Walton el
Gracias. Sin embargo, esto no aborda cómo obtener la casilla de verificación seleccionada previamente. También gracias, no sabía que console.log no funcionaba en IE.
Mateo
No duplique en los controladores de eventos - en los navegadores que tanto el fuego clicky changecontroladores de eventos, que va a terminar llama al gestor dos veces. En este caso, dado que @Matthew está preocupado por el valor anterior, eso podría tener malas consecuencias.
Nate Cook
3
@NateCook claramente te perdiste el punto de mi ejemplo. Estaba mostrando que cada vez que se activa el evento de cambio, también se activa el evento de clic. Solo porque estaba usando ambos eventos en mi ejemplo no significa que lo estaba promocionando. Creo que tu voto negativo no fue merecido.
Philip Walton
Pero lo estaba promocionando: su respuesta original decía: "Entonces, mi punto es que, aunque el evento de cambio se active en algunos navegadores, el evento de clic debe proporcionar la cobertura que necesita como respaldo, y puede usar el cambio en los navegadores". que lo apoyan ". clickes el controlador correcto aquí y el único que necesita. Su revisión aclara eso, así que estoy eliminando mi voto negativo.
Nate Cook
10

¿Qué pasa con el uso del evento de cambio de 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
fuente
$(this).val()también se puede reemplazar con javascript nativo e.currentTarget.value, donde e es el objeto de evento pasado a través de la función de devolución de llamada.
Eric
6

Como puede ver aquí: http://www.w3schools.com/jsref/event_onchange.asp El atributo onchange no es compatible con los botones de opción.

La primera pregunta SO vinculada por usted le da la respuesta: use el onclickevento en su lugar y verifique el estado del botón de radio dentro de la función que activa.

Constantin Groß
fuente
Esto es solo un poco incompleto. Entonces, después de crear una función para manejar todos los clics en el grupo, ¿cómo verificaría cuál ha sido deseleccionado? Además, estoy buscando una solución que no sea jquery.
Mateo
Se marcará el botón de opción seleccionado, todos los demás miembros del mismo grupo se desmarcarán. O bien, puede recorrer todos los botones de radio de ese grupo y verificar su estado. Si proporcionó más información sobre lo que desea hacer con los botones de radio, sería más fácil decirle cómo continuar.
Constantin Groß
2
onclickno es la misma semántica, ya que puede cambiar la comprobación de un botón de radio a través de la entrada del teclado si se habilita la selección de forma del teclado.
gsnedders
Sin embargo, @gsnedders onclick parece funcionar a través de la entrada del teclado en la mayoría de los navegadores: Firefox, IE, Safari, Chrome. Es extraño que haga lo que se supone que debe hacer onchange, pero lo hace. Sin embargo, tengo curiosidad por saber cómo funciona esto en plataformas móviles.
Mateo
1
w3schools para 1 y no abordó todo el problema para 2, que era cómo obtener la radio previamente seleccionada. 2 fue la parte más importante de la pregunta en mi mente.
Mateo
6

No creo que haya otra forma que no sea almacenar el estado anterior. Aquí está la solución con 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" />

Después de pensarlo un poco más, decidí deshacerme de la variable y agregar / eliminar clase. Esto es lo que obtuve: http://jsfiddle.net/BeQh3/2/

Alex Okrushko
fuente
1
Pensé en usar un jQuery y definitivamente hay mucho más que podría hacerse con él. Puede vincular algunos eventos personalizados para verificarlos y no marcarlos, e incluso verificar las peculiaridades del navegador cruzado. Suena como una buena idea para un complemento.
Mateo
Gracias. Me gusta jQuery pero no está disponible para mí en este caso.
Matthew
¿Cómo no está disponible jQuery para usted?
jmort253
1
Si jQuery no estuviera disponible para mí, encontraría un trabajo diferente.
Adrian Carr
5

Sí, no hay evento de cambio para el botón de opción seleccionado actualmente. Pero el problema es cuando cada botón de radio se toma como un elemento separado. En cambio, un grupo de radio debe considerarse un elemento único como select. Por lo tanto, se activa el evento de cambio para ese grupo. Si es un selectelemento, nunca nos preocupamos por cada opción, sino que solo tomamos la opción seleccionada. Almacenamos el valor actual en una variable que se convertirá en el valor anterior, cuando se selecciona una nueva opción. De manera similar, debe usar una variable separada para almacenar el valor del botón de opción marcado.

Si desea identificar el botón de radio anterior, debe realizar un bucle en el mousedownevento.

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

mira esto: http://jsfiddle.net/diode/tywx6/2/

Diodo
fuente
Pensé en usar el mousedown para verificar antes de que ocurriera el clic, pero eso no funcionará cuando use el espacio para seleccionar la radio. Tal vez el evento de enfoque en lugar de o además de lo que mencionaste.
Mateo
5

Almacene la radio marcada anterior en una variable:
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 DE EJEMPLO 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
fuente
Gracias. Podría llevar el violín incluso un poco más lejos y almacenar la radio en sí. De esta manera, cuando el valor cambia, tiene una referencia a la radio seleccionada previamente en lugar de solo su valor.
Mateo
@Matthew En realidad, el violín que compartí fue almacenar el elemento de radio y no solo el valor. El valor se registró para mostrar el propósito del código, pero no se almacenó para su uso posterior. Estoy editando los nombres de las variables para aclarar más: previousChecked -> previousCheckedRadio, that -> currentCheckedRadio;
Dimitar Bonev
Gracias. Esta es una buena solución, excepto para usar el evento change que no se comporta correctamente entre navegadores. Si usaste clic en su lugar, sería mejor.
Matthew
5

Me doy cuenta de que este es un problema antiguo, pero este fragmento de código funciona para mí. Quizás alguien en el futuro lo encuentre ú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
fuente
1
Si agrega onkeydown="radioChange('juju','button1',radioArray);", también podría capturar cuando un usuario usa espacio para revisar la radio.
Mateo
2

Esto está justo en la parte superior de mi cabeza, pero podría hacer un evento onClick para cada botón de radio, darles todas las ID diferentes y luego hacer un bucle for en el evento para pasar por cada botón de radio en el grupo y encontrar cuál es se verificó mirando el atributo 'verificado'. La identificación de la marcada se almacenaría como una variable, pero es posible que desee usar una variable temporal primero para asegurarse de que el valor de esa variable haya cambiado, ya que el evento de clic activará si se marcó o no un nuevo botón de opción.

Andrew Latham
fuente
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
fuente
2

Si desea evitar el guión en línea, simplemente puede escuchar un evento de clic en la radio. Esto se puede lograr con Javascript simple escuchando un evento de clic en

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

Puede agregar el siguiente 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
fuente
1

esto funciona para mi

<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
fuente
Creo que esta debería ser la respuesta ahora que es compatible.
Leo
0

Esta es la función más fácil y más eficiente de usar, simplemente agregue tantos botones como desee al marcado = falso y haga que el evento onclick de cada botón de radio llame a esta función. Designe un número único para cada botón de radio

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

La manera más fácil y poderosa

leer solo entradas de radio 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î
fuente