jQuery Click se déclenche deux fois en cliquant sur l'étiquette

114

J'utilise jQuery pour créer des boutons radio personnalisés et j'ai un problème. Lorsque vous cliquez sur l'étiquette associée à la radio, les événements de clic se déclenchent deux fois, si je ne clique que sur la radio elle-même, cela fonctionne bien (en fait, ce n'est pas la radio sur laquelle je clique mais le div qui enveloppe toute l'entrée et l'étiquette). Voici le code:

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

Y a-t-il une solution à ce comportement?

Ton
la source

Réponses:

130

Essayez d'ajouter:

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

au .bind () ou .click (), selon ce que vous voyez. Ajoutez également le paramètre evtà la fonction, commefunction(evt) {...

Jordan
la source
9
Pourquoi cela arrive-t-il?
chovy
8
Parce qu'il y a des éléments imbriqués. Chaque élément de la hiérarchie fera remonter l'événement.
Jordanie
7
Si vous utilisez ceci pour une case à cocher, cela était également nécessaire pour que je puisse réellement vérifier l'entrée:jQuery('input').prop('checked', true);
David Sinclair
6
return false;équivaut à `evt.stopPropagation (); evt.preventDefault (); ( dans jQuery )
basilic
193

J'ai essayé d'ajouter la solution ci-dessus en ajoutant:

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

mais n'a pas fonctionné. Cependant en ajoutant ceci:

evt.stopImmediatePropagation();

résolu le problème! :)

N3da
la source
6
Je ne sais pas pourquoi, mais ce que vous avez dit semble fonctionner pour mon code. Je vous remercie!
shaosh
8
Parfait. Cela a bien fonctionné! Bien que non evt.stopPropagation(); evt.preventDefault();; 't
James111
1
seulement cela a fonctionné pour moi, j'avais essayé les autres fonctions sans succès
gardarvalur
1
Cette réponse m'a sauvé la vie! Merci: D
Bluetree
1
C'était la réponse pour moi!
Dyluck
73

Liez l'événement de clic à l'entrée plutôt qu'à l'étiquette. Lorsque vous cliquez sur l'étiquette, l'événement se produira toujours car, comme Dustin l'a mentionné, un clic sur l'étiquette déclenche un clic sur l'entrée. Cela permettra à l'étiquette de conserver sa fonctionnalité normale.

$('input').click();

Au lieu de

$('label').click();
dougmacknz
la source
10
Cette solution fonctionne également si votre balisage utilise la technique du label-wrapping-the-input, et vient de sauver ma raison: o)
Whelkaholism
4
vous devriez en fait vous lier changemême sur le bouton radio car le texte d'une étiquette est cliquable - ils ne cliquent pas toujours sur le bouton radio lui-même.
chovy
2
Si vous utilisez une label > inputconfiguration Bootstrapesque , c'est LA réponse, pas une réponse. L'ajout de evt.stopPropagation()ou evt.preventDefault()à votre code, bien qu'efficace, est un piratage à éviter lorsque la solution appropriée est beaucoup plus propre et plus efficace.
elPastor
wow wow juste wow, j'ai passé presque quelques jours à comprendre, enfin cela a aidé et merci beaucoup pour l'explication
opensource-developer
cela m'a sauvé la journée!
gab06
11

Si vous essayez d'utiliser un conteneur externe comme élément de clic, vous pouvez également laisser les événements bouillonner naturellement et tester l'élément attendu dans votre gestionnaire de clic. Ce scénario est utile si vous essayez de styliser une zone de clic unique pour un formulaire.

<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>
tobius
la source
1
Cela a fonctionné pour moi car je voulais toujours que le bouton radio soit sélectionné. Je ne voulais tout simplement pas que le gestionnaire d'événements fasse tout deux fois.
chovy
1
Cela ne permettra pas aux enfants d'être sélectionnés. BTW, une meilleure option pour obtenir les mêmes fonctionnalités seraitif (e.target == e.currentTarget) {}
Chicken Soup
9

Pour résoudre ce problème facilement, supprimez l'attribut "for" sur l'étiquette. Un clic sur l'étiquette déclenchera également un clic sur l'élément associé. (qui, dans votre cas, déclenche deux fois votre événement de clic.)

Bonne chance

Dustin
la source
En effet, la solution rapide. On pourrait faire valoir que cela dérange le balisage, mais je rétorque que le but de l'attribut "for" est la propagation d'événements. Donc, si vous utilisez un autre mécanisme (jquery), alors débarrassez-vous de "for".
Chris Harrington
5

J'utilise habituellement cette syntaxe

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

au lieu de

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

La meilleure réponse est cachée dans les commentaires:

vous devriez en fait vous lier changemême sur le bouton radio car le texte d'une étiquette est cliquable - ils ne cliquent pas toujours sur le bouton radio lui-même. - chovy 12 décembre 13 à 1:45

Ce violon montre que toutes les autres solutions - stopPropagation, stopImmediatePropagation, preventDefault, return false- soit rien changer ou détruire la case à cocher / fonctionnalité radio). Cela montre également qu'il s'agit d'un problème JavaScript vanille, pas d'un problème jQuery.

EDIT: Une autre solution de travail que je viens de trouver dans un autre thread est de lier le onclickà l'entrée plutôt qu'à l'étiquette. Violon mis à jour .

WoodrowShigeru
la source
2

J'ai essayé en ajoutant une solution.

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

mais n'a pas fonctionné.

En ajoutant

evt.stopImmediatePropagation();

résolu le problème! :)

Janki Moradiya
la source
1

Le problème avec e.preventDefault (); est-ce que cela empêche le clic d'étiquette de vérifier le bouton radio.

Une meilleure solution serait d'ajouter simplement une vérification rapide «est vérifié» comme ceci:

$("label").click(function(e){
  var rbtn = $(this).find("input");
  if(rbtn.is(':checked')){
  **All the code you want to have happen on click**
  }
)};
yoshyosh
la source
1
Une solution encore plus succincte consisterait simplement à utiliser .mouseup plutôt que .click
yoshyosh
1
Cette solution ne fonctionne pas si votre code va également permettre à un utilisateur de désélectionner un bouton radio. Le double feu provoque dans ce cas de graves problèmes.
Johncl
1

Mon problème est un peu différent, car evt.stopPropagation();evt.preventDefault();cela ne fonctionne pas pour moi, j'ajoute simplement return false;à la fin, puis cela fonctionne.

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

Dans mon cas, le problème était que j'avais l'événement de clic dans une fonction et que la fonction était exécutée deux fois .... chaque exécution de la fonction crée un nouvel événement de clic. - facepalm -

après avoir déplacé l'événement de clic en dehors de la fonction, tout a fonctionné comme prévu! :)

FalcoB
la source
0

Essayez de placer votre balise d'entrée en dehors de l'élément déclencheur, car la balise d'étiquette émule le clic, vous aurez donc toujours plus d'un appel.

Benjamin
la source
0

J'ai eu le même problème parce que j'avais imbriqué ma radio dans l'étiquette comme celle-ci avec le gestionnaire attaché à radio_div. La suppression de l'étiquette imbriquée a résolu le problème.

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

L'étiquette déclenche la case à cocher / radio à cocher.

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

Il empêche surtout l'étiquette de déclencher ce comportement.

Kevin.B
la source
0

Le clic sur l'étiquette avec un attribut for = "some-id" déclenche un nouveau clic, mais uniquement si la cible existe et est une entrée. Je n'ai pas pu le résoudre parfaitement avec e.preventDefault () ou des trucs comme ça, alors je l'ai fait comme ceci:

Par exemple, si vous avez cette structure et souhaitez un événement en cliquant sur .some-class

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

Ce qui a fonctionné était:

$(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
la source