jQuery Click si attiva due volte quando si fa clic sull'etichetta


114

Sto usando jQuery per creare pulsanti di opzione personalizzati e ho un problema. Quando si fa clic sull'etichetta associata alla radio, gli eventi di clic si attivano due volte, se faccio clic solo sulla radio stessa funziona bene (beh, in realtà non è la radio su cui sto facendo clic ma il div che avvolge l'intero input e l'etichetta). Ecco il codice:

L'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>

C'è qualche soluzione per questo comportamento?

Risposte:


130

Prova ad aggiungere:

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

a .bind () o .click (), a seconda di quale sia. Inoltre, aggiungi il parametro evtalla funzione, comefunction(evt) {...


9
perché succede questo?
chovy

8
Perché ci sono elementi annidati. Ogni elemento nella gerarchia farà esplodere l'evento.
Jordan

7
Se stai usando questo per una casella di controllo, questo era necessario anche per me per abilitare l'ingresso per essere effettivamente controllato:jQuery('input').prop('checked', true);
David Sinclair,

6
return false;è equivalente a `evt.stopPropagation (); evt.preventDefault (); ( in jQuery )
basil

193

Ho provato ad aggiungere la soluzione sopra aggiungendo:

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

ma non ha funzionato. Tuttavia aggiungendo questo:

evt.stopImmediatePropagation();

problema risolto! :)


6
Non so perché, ma quello che hai detto sembra funzionare per il mio codice. Grazie!
shaosh

8
Perfetto. Ha funzionato bene! Anche se evt.stopPropagation(); evt.preventDefault();non; 't
James111

1
solo questo ha funzionato per me, avevo provato le altre funzioni senza successo
gardarvalur

1
Questa risposta mi ha salvato la vita! Grazie: D
Bluetree

1
Questa è stata la risposta per me!
Dyluck

73

Associa l'evento clic all'input anziché all'etichetta. Quando si fa clic sull'etichetta, l'evento si verificherà ancora perché, come ha detto Dustin, un clic sull'etichetta attiva un clic sull'input. Ciò consentirà all'etichetta di mantenere la sua normale funzionalità.

$('input').click();

Invece di

$('label').click();

10
Questa soluzione funziona anche se il tuo markup utilizza la tecnica di avvolgimento dell'etichetta e ha appena salvato la mia sanità mentale: o)
Whelkaholism

4
in realtà dovresti associarti changeanche al pulsante di opzione poiché il testo di un'etichetta è selezionabile: non sempre fanno clic sul pulsante di opzione stesso.
chovy

2
Se si utilizza una label > inputconfigurazione Bootstrapesque , questa è LA risposta, non una risposta. L'aggiunta di evt.stopPropagation()o evt.preventDefault()al codice, sebbene efficace, è un trucco che dovrebbe essere evitato quando la soluzione corretta è molto più pulita ed efficiente.
elPastor

wow wow semplicemente wow, ho passato quasi un paio di giorni a capirlo, finalmente questo mi ha aiutato e grazie mille per la spiegazione
opensource-developer

questo mi ha salvato la giornata!
gab06

11

Se stai tentando di utilizzare un contenitore esterno come elemento clic, puoi anche lasciare che gli eventi si gonfino in modo naturale e testare l'elemento previsto nel gestore dei clic. Questo scenario è utile se stai cercando di definire un'area di clic univoca per un modulo.

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

1
Questo ha funzionato per me perché volevo ancora che il pulsante di opzione fosse selezionato. Semplicemente non volevo che il gestore eventi eseguisse tutto due volte.
chovy

1
Ciò non consentirà di selezionare i bambini. A proposito, un'opzione migliore per ottenere la stessa funzionalità sarebbeif (e.target == e.currentTarget) {}
Chicken Soup

9

Per risolvere il problema nel modo più semplice, rimuovere l'attributo "for" sull'etichetta. Un clic sull'etichetta attiverà anche un clic sull'elemento associato. (che nel tuo caso attiva due volte il tuo evento clic.)

In bocca al lupo


In effetti la soluzione rapida. Si potrebbe sostenere che stia incasinando il markup, ma ribattere che lo scopo dell'attributo "for" è la propagazione degli eventi. Quindi, se stai usando un altro meccanismo (jquery), allora sbarazzati di "for".
Chris Harrington

5

Di solito uso questa sintassi

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

invece di

.click(function () { console.log('click'); })

5

La migliore risposta è nascosta nei commenti:

in realtà dovresti associarti changeanche al pulsante di opzione poiché il testo di un'etichetta è selezionabile: non sempre fanno clic sul pulsante di opzione stesso. - chovy 12 dicembre 13 alle 1:45

Questo violino dimostra che tutte le altre soluzioni - stopPropagation, stopImmediatePropagation, preventDefault, return false- sia cambiare nulla o distruggere la funzionalità casella di controllo / radio). Illustra anche che questo è un problema JavaScript vanigliato, non un problema jQuery.

EDIT: un'altra soluzione funzionante che ho appena trovato in un altro thread è associare onclickl'input piuttosto che l'etichetta. Violino aggiornato .


2

Ho provato aggiungendo la soluzione.

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

ma non ha funzionato.

Aggiungendo

evt.stopImmediatePropagation();

problema risolto! :)


1

Il problema con e.preventDefault (); è che impedisce al clic sull'etichetta di controllare il pulsante di opzione.

Una soluzione migliore sarebbe semplicemente aggiungere un controllo rapido "è controllato" in questo modo:

$("label").click(function(e){
  var rbtn = $(this).find("input");
  if(rbtn.is(':checked')){
  **All the code you want to have happen on click**
  }
)};

1
Una soluzione ancora più succinta sarebbe semplicemente usare .mouseup anziché
.click

1
Questa soluzione non funziona se il codice consentirà anche a un utente di deselezionare un pulsante di opzione. Il doppio fuoco provoca gravi problemi in questo caso.
Johncl

1

Il mio problema è un po 'diverso, dato evt.stopPropagation();evt.preventDefault();che per me non funziona, aggiungo solo return false;alla fine, poi funziona.

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

1

Nel mio caso il problema era che avevo l'evento click in una funzione e la funzione è stata eseguita due volte .... ogni esecuzione della funzione crea un nuovo evento click. - facepalm -

dopo aver spostato l'evento click fuori dalla funzione, tutto ha funzionato come previsto! :)


0

Prova a inserire il tag di input all'esterno dell'elemento trigger, perché il tag label emula il clic, quindi avrai sempre più di una chiamata.


0

Ho avuto lo stesso problema perché avevo annidato la mia radio all'interno dell'etichetta in questo modo con il gestore collegato a radio_div. La rimozione dell'etichetta nidificata ha risolto il problema.

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

0

L'etichetta attiva il controllo della radio / casella di controllo.

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

Impedisce soprattutto all'etichetta di attivare questo comportamento.


0

Il clic sull'etichetta con un attributo for = "some-id" attiva un nuovo clic, ma solo se l'obiettivo esiste ed è un input. Non sono riuscito a risolverlo perfettamente con e.preventDefault () o cose del genere, quindi l'ho fatto in questo modo:

Ad esempio, se hai questa struttura e desideri un evento facendo clic su .some-class

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

Quello che ha funzionato è stato:

$(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
    })
;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.