jQuery Click se dispara dos veces al hacer clic en la etiqueta

114

Estoy usando jQuery para crear botones de opción personalizados y tengo un problema. Al hacer clic en la etiqueta que se asoció con la radio, los eventos de clic se disparan dos veces, si hago clic solo en la radio, está funcionando bien (bueno, en realidad no es la radio en la que estoy haciendo clic, sino el div que envuelve toda la entrada y la etiqueta). Aquí está el código:

El HTML:

 <div id="box">
     <asp:RadioButtonList ID="RadioButtonList1" runat="server">
         <asp:ListItem>RADIO1</asp:ListItem>
         <asp:ListItem>RADIO2</asp:ListItem>
         <asp:ListItem>RADIO3</asp:ListItem>
     </asp:RadioButtonList>
</div>

jQuery:

<script type="text/javascript">
       $(function () {
            $('#box').find('input:radio').each(function (i) {

            var input = $(this);
            // get the associated label using the input's id
            var label = $('label[for=' + input.attr('id') + ']');
            // wrap the input + label in a div
            $('<div class="custom-radio"></div>').insertBefore(input).append(label, input);

            var wrapperDiv = input.parent();

            // find all inputs in this set using the shared name attribute
            var allInputs = $('input[name=' + input.attr('name') + ']');

            // necessary for browsers that don't support the :hover pseudo class on labels
            label.hover(

            function () {
                $(this).addClass('hover');
            }, function () {
                $(this).removeClass('hover checkedHover');
            });

            //bind custom event, trigger it, bind click,focus,blur events
            wrapperDiv.bind('updateState', function () {
                if ($(this)[0].children[1].checked) {
                    allInputs.each(function () {
                        var curDiv = $('div > label[for=' + $(this).attr('id') + ']').parent();
                        curDiv.removeClass('custom-radio-checked');
                        curDiv.addClass('custom-radio');
                    });
                    $(this).toggleClass('custom-radio custom-radio-checked');
                }
                else {
                    $(this).removeClass('custom-radio-checked checkedHover checkedFocus');
                }

            })
            .trigger('updateState')
            .click(function () { console.log('click'); })
            .focus(function () {
                label.addClass('focus');
            }).blur(function () {
                label.removeClass('focus checkedFocus');
            });
        }); 
       });
   </script>

¿Existe alguna solución para este comportamiento?

tono
fuente

Respuestas:

130

Intente agregar:

evt.stopPropagation();
evt.preventDefault();

al .bind () o .click (), lo que esté viendo. Además, agregue el parámetro evta la función, comofunction(evt) {...

Jordán
fuente
9
¿Por qué pasó esto?
Chovy
8
Porque hay elementos anidados. Cada elemento de la jerarquía aumentará el evento.
Jordania
7
Si está usando esto para una casilla de verificación, esto también fue necesario para que la entrada se verifique realmente:jQuery('input').prop('checked', true);
David Sinclair
6
return false;es equivalente a `evt.stopPropagation (); evt.preventDefault (); ( en jQuery )
basil
193

Intenté agregar la solución anterior agregando:

evt.stopPropagation();
evt.preventDefault();

pero no funcionó. Sin embargo, agregando esto:

evt.stopImmediatePropagation();

¡resuelve el problema! :)

N3da
fuente
6
No sé por qué, pero lo que dijiste parece funcionar para mi código. ¡Gracias!
shaosh
8
Perfecto. ¡Esto funcionó bien! Aunque evt.stopPropagation(); evt.preventDefault();no lo hizo; 't
James111
1
solo esto funcionó para mí, había probado las otras funciones sin éxito
gardarvalur
1
¡Esta respuesta me salvó la vida! Gracias: D
Bluetree
1
¡Esta fue la respuesta para mí!
Dyluck
73

Vincula el evento de clic a la entrada en lugar de a la etiqueta. Cuando se hace clic en la etiqueta, el evento seguirá ocurriendo porque, como mencionó Dustin, un clic en la etiqueta activa un clic en la entrada. Esto permitirá que la etiqueta mantenga su funcionalidad normal.

$('input').click();

En vez de

$('label').click();
dougmacknz
fuente
10
Esta solución también funciona si su marcado usa la técnica de envoltura de etiquetas de entrada, y acaba de salvar mi cordura: o)
Whelkaholism
4
en realidad, debería enlazar changeincluso en el botón de opción, ya que se puede hacer clic en el texto de una etiqueta; no siempre hacen clic en el botón de opción.
Chovy
2
Si usa una label > inputconfiguración Bootstrapesque , esta es LA respuesta, no una respuesta. Agregar evt.stopPropagation()o evt.preventDefault()a su código, si bien es efectivo, es un truco que debe evitarse cuando la solución adecuada es mucho más limpia y eficiente.
elPastor
wow wow simplemente wow, pasé casi un par de días averiguando, finalmente esto ayudó y muchas gracias por la explicación
desarrollador de código abierto
esto salvó mi día!
gab06
11

Si está tratando de usar un contenedor externo como un elemento de clic, también puede dejar que los eventos burbujeen naturalmente y probar el elemento esperado en su controlador de clic. Este escenario es útil si está intentando diseñar una zona de clic única para un formulario.

<form>
<div id="outer">
    <label for="mycheckbox">My Checkbox</label>
    <input type="checkbox" name="mycheckbox" id="mycheckbox" value="on"/>
</div>
</form>
<script>
$('#outer').on('click', function(e){
    // this fires for #outer, label, and input
    if (e.target.tagName == 'INPUT'){
        // only interested in input
        console.log(this);
    }
});
</script>
tobio
fuente
1
Esto funcionó para mí ya que todavía quería que se seleccionara el botón de opción. Simplemente no quería que el controlador de eventos hiciera todo dos veces.
Chovy
1
Esto no permitirá que se seleccionen niños. Por cierto, una mejor opción para lograr la misma funcionalidad seríaif (e.target == e.currentTarget) {}
Sopa de pollo
9

Para solucionar este problema de la manera más sencilla, elimine el atributo "para" de la etiqueta. Un clic en la etiqueta también activará un clic en el elemento asociado. (que en su caso dispara su evento de clic dos veces).

Buena suerte

Dustin
fuente
De hecho, la solución rápida. Se podría argumentar que está jugando con el marcado, pero yo diría que el propósito del atributo "para" es la propagación de eventos. Entonces, si está utilizando otro mecanismo (jquery), elimine "for".
Chris Harrington
5

Yo suelo usar este Synthax

.off('click').on('click', function () { console.log('click'); })

en vez de

.click(function () { console.log('click'); })
Dalibor
fuente
5

La mejor respuesta está oculta dentro de los comentarios:

en realidad, debería enlazar changeincluso en el botón de opción, ya que se puede hacer clic en el texto de una etiqueta; no siempre hacen clic en el botón de opción. - Chovy 12 de diciembre de 2013 a la 1:45

Este violín ilustra que todas las otras soluciones - stopPropagation, stopImmediatePropagation, preventDefault, return false- ya sea el cambio nada o destruir la funcionalidad casilla / radio). También ilustra que se trata de un problema básico de JavaScript, no de jQuery.

EDITAR: Otra solución de trabajo que acabo de encontrar en otro hilo es vincular onclickla entrada en lugar de la etiqueta. Violín actualizado .

WoodrowShigeru
fuente
2

Lo he intentado agregando solución.

evt.stopPropagation();
evt.preventDefault();

pero no funcionó.

Añadiendo

evt.stopImmediatePropagation();

¡resuelve el problema! :)

Janki Moradiya
fuente
1

El problema con e.preventDefault (); es que evita que el clic de la etiqueta marque el botón de opción.

Una mejor solución sería simplemente agregar una verificación rápida "está marcada" como esta:

$("label").click(function(e){
  var rbtn = $(this).find("input");
  if(rbtn.is(':checked')){
  **All the code you want to have happen on click**
  }
)};
yoshyosh
fuente
1
Una solución aún más sucinta sería simplemente usar .mouseup en lugar de .click
yoshyosh
1
Esta solución no funciona si su código también permitirá que un usuario anule la selección de un botón de opción. El doble fuego causa serios problemas en este caso.
Johncl
1

Mi problema es un poco diferente, ya evt.stopPropagation();evt.preventDefault();que no funciona para mí, solo agrego return false;al final, luego funciona.

$("#addressDiv").on("click", ".goEditAddress", function(event) {
    alert("halo");
    return false;
});
GMsoF
fuente
1

En mi caso, el problema era que tenía el evento de clic en una función y la función se ejecutó dos veces ... cada ejecución de la función crea un nuevo evento de clic. - palma de la mano -

después de mover el evento de clic fuera de la función, ¡todo funcionó como se esperaba! :)

FalcoB
fuente
0

Intente poner su etiqueta de entrada fuera del elemento disparador, porque la etiqueta de etiqueta emula el clic, por lo que siempre tendrá más de una llamada.

Benjamín
fuente
0

Tuve el mismo problema porque había anidado mi radio dentro de la etiqueta de esta manera con el controlador adjunto a radio_div. La eliminación de la etiqueta anidada solucionó el problema.

<div id="radio_div">
    <label>
       <input type="radio" class="community_radio" name="community_radio" value="existing">
           Add To Existing Community
    </label>
</div>
shaw2thefloor
fuente
0

La etiqueta activa la casilla de verificación / radio para marcar.

if ($(event.target).is('label')){
    event.preventDefault();
}

Impide especialmente que la etiqueta desencadene este comportamiento.

Kevin.B
fuente
0

El clic en la etiqueta con un atributo for = "some-id" activa un nuevo clic, pero solo si el objetivo existe y es una entrada. Estaba no lo pueda resolver perfectamente con e.preventDefault () o cosas por el estilo así que lo hice de esta manera:

Por ejemplo, si tiene esta estructura y desea un evento al hacer clic en .some-class

<div class="some-class">
    <input type=checkbox" id="the-input-id" />
    <label for="the-input-id">Label</label>
</div>

Lo que funcionó fue:

$(document)
    .on('click', '.some-class', function(e) {
        if(
            $(e.target).is('label[for]')
            &&
            $('input#' + $(e.target).attr('for')).length
        ) {
            // This will trigger a new click so we are out of here
            return;
        }

        // else do your thing here, it will not get called twice
    })
;
Julesezaar
fuente