web-dev-qa-db-fra.com

L'événement de clic Jquery se déclenche deux fois lorsque l'utilisateur clique sur un libellé HTML

J'ai un conteneur div qui contient une case à cocher html et son étiquette . En utilisant jquery, je voulais déclencher un événement click lorsque quelqu'un clique sur une étiquette dans ce conteneur.

Je vois que jQuery clic déclenche deux fois lorsque je clique sur l'étiquette!

À des fins de test, j'ai déclenché l'événement click sur la case à cocher au lieu de label

Voici le violon . http://jsfiddle.net/AnNvJ/2/

<tr>
    <td>
        <div id="test">
            <label>
                <input type="checkbox" id="1" />demo1</label>
            <label>
                <input type="checkbox" id="2" />demo2</label>
            <label>
                <input type="checkbox" id="3" />demo3</label>
        </div>
    </td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
</tr>

JQUERY

$(document).ready(function () {

    $('#test label').live('click', function (event) {
        alert('clicked');
    });


    $('#test checkbox').live('click', function (event) {
        alert('clicked');
    });

});
27
sravis

Celui de $('#test checkbox') n'est jamais appelé car vous n'avez pas de balise nommée checkbox.

Et selon que vous cliquiez sur la case à cocher ou l'étiquette, le rappel pour $('#test label') est appelé une ou deux fois. cause du bouillonnement parce que l'élément input fait partie de l'étiquette et qu'il est un union et qu'il reçoit donc également l'événement si l'étiquette est un clic (il ne bouillonne pas, vous ne pouvez pas faire event.stopPropagation()).

Vous pouvez vérifier cela si vous changez votre code de cette façon:

 $('#test label').live('click', function (event) {
        event.stopPropagation(); //<-- has no effect to the described behavior
        console.log(event.target.tagName);
 });

Cliquez sur l'étiquette:

LABEL
CONTRIBUTION

Cliquez sur l'entrée:

CONTRIBUTION

EDIT Si vous souhaitez uniquement gérer le clic sur la variable label et non la case à cocher - et que vous ne pouvez pas modifier votre structure HTML - vous pouvez simplement ignorer l'événement de l'élément input .

Soit de cette façon:

$('#test label').live('click', function (event) {
    if( event.target.tagName === "LABEL" ) {
         alert('clicked');
    }
});

Ou en utilisant jQuery pour tester:

$('#test label').live('click', function (event) {
    if( $(event.target).is("label") ) {
         alert('clicked');
    }
});
38
t.niese

C'est parce que vous avez mis l'entrée dans le libellé de sorte que lorsque vous cliquez sur la case à cocher, vous cliquez également sur tous les parents (cela s'appelle bouillonner) EDIT, crédit à @ t.niese: en fait, il n’ya pas de bouillonnement ici car le problème est que vous cliquez sur l’étiquette et que cela ne fait que bouillonner.

Pour empêcher un double-clic mais cochez également la case, vous pouvez utiliser:

$(document).ready(function () {

    $('#test label').on('click', function (event) {
        event.preventDefault();
        var $check = $(':checkbox', this);
        $check.prop('checked', !$check.prop('checked'));
        alert('clicked label');
    });


    $('#test :checkbox').on('click', function (event) {
        event.stopPropagation();
        alert('clicked checkbox');
    });
});

VIOLON

Préférez également l'utilisation de $.on et notez l'utilisation de :checkbox selector ou [type="checkbox"] qui, selon l'API JQuery, est plus rapide et plus compatible ( sélecteurs d'attributs )

event.preventDefault() arrêtera toutes les actions gérées par le navigateur pour la balise native event.stopPropagation() empêche le bouillonnement et les gestionnaires parents d'être avertis de l'événement.

9
TecHunter

L'OP a trouvé une solution pour empêcher le gestionnaire d'événements click de l'étiquette d'être exécuté deux fois pour un seul clic. La réponse sélectionnée a également une bonne solution, mais elle indique à tort que cela n'a rien à voir avec la propagation d'événements.

La raison pour laquelle il y a deux clics est liée à la propagation d'événements.

En cliquant sur une étiquette associée à une entrée, deux événements de clic sont déclenchés. Le premier événement de clic est déclenché pour l'étiquette. Le traitement par défaut de cet événement de clic déclenche le déclenchement d'un deuxième événement de clic pour l'entrée associée. Si l'entrée est un descendant de l'étiquette, le deuxième événement de clic se propage jusqu'à l'étiquette. C'est pourquoi le gestionnaire d'événements click pour l'étiquette est appelé deux fois.


Par conséquent, une façon de résoudre ce problème (comme l'OP l'a découvert) consiste à ne pas placer l'entrée dans l'élément label. Il y aura toujours deux événements de clic, mais le deuxième événement de clic ne fera pas l'objet d'une bulle entre l'entrée et le libellé.

Lorsque l'entrée n'est pas à l'intérieur de l'étiquette, l'attribut "pour" de l'étiquette peut être utilisé pour associer l'étiquette à l'entrée. L'attribut "pour" doit être défini sur la valeur "id" de l'entrée.

<input type="checkbox" id="1" />
<label for="1">demo1</label>

Une autre solution serait d’empêcher l’événement de remonter de l’entrée:

$('#test :checkbox').on('click', function(event) {
    event.stopPropagation();
});

$('#test label').on('click', function() {
    alert('clicked');
});

Il y a ensuite la solution présentée dans la réponse sélectionnée, qui consiste à faire en sorte que le gestionnaire de clic de l'étiquette ignore l'événement lorsqu'il déborde de l'entrée. Je préfère l'écrire comme ceci:

$('#test label').on('click', function(event) {
    if (event.target == this) {
        alert('clicked');
    }
});
3
John S

Vous DEVEZ utiliser preventDefault dans votre étiquette et stopPropagation dans votre case à cocher:

http://jsfiddle.net/uUZyn/5/

Le code:

$(document).ready(function () {

    $('#test label').on('click', function (event) {
      event.preventDefault();        
      alert('clicked label');
      var ele = $(this).find('input');
    if(ele.is(':checked')){
      ele.prop('checked', false);        
    }else{
      ele.prop('checked', true);
    }
  });

  $('#test :checkbox').on('click', function (event) {
    event.stopPropagation();
    alert('clicked checkbox');
  });
});

De cette façon, vous évitez le comportement @ explique t.niese

Merci aux réponses précédentes, je ne comprendrais pas sans: D.

Mettre à jour

Comme le fait remarquer t.niese, voici la réponse que j'ai mise à jour avec le comportement:

http://jsfiddle.net/uUZyn/6/

Vient d’ajouter le comportement de vérification après l’utilisation de preventDefault XD

1
Chococroc

Je viens de rencontrer le même problème où cliquer sur l'étiquette déclenche deux événements de clic distincts. J'ai pu résoudre le problème moi-même en ajoutant un attribut name à l'entrée et un attribut for à l'étiquette.

Par exemple:

<div id="test">
    <label for="checkbox1">
        <input type="checkbox" name="checkbox1" id="1" />demo1</label>
    <label for="checkbox2">
        <input type="checkbox" name="checkbox2" id="2" />demo2</label>
    <label for="checkbox3">
        <input type="checkbox" name="checkbox3" id="3" />demo3</label>
 </div>

Pas de JavaScript nécessaire.

0
Will Hitchcock

Cela se produit principalement lorsque vous cliquez sur l'élément X ayant l'élément enfant Y et que l'utilisateur clique sur Y.

L'événement diffusé à partir de l'élément réellement cliqué est un événement indésirable que vous ne souhaitez pas.

Vous avez deux options pour éviter ce double déclenchement ...

1) Utilisez $ ('sélecteur'). TriggerHandler ('any_standard_event_name') - Ici, par événement standard, je veux dire 'clic', 'changement', 'survol', etc. (Je n'ai jamais essayé cette option)

2) Utilisez le nom de l'événement personnalisé. par exemple. $ ('sélecteur'). trigger ('mon.click');

En savoir plus sur http://api.jquery.com/trigger/

0
EGL 2-101

En ce qui concerne mon cas et le fait que j'utilise le bouton md de Material Design, les suggestions ci-dessus ne fonctionnaient pas très bien, car elles réduisaient considérablement l'empreinte de la zone recevant une pression du doigt sur mon Android. Sans parler de 2 noms de balises au moins, j'en aurais parfois 3 (BUTTON, SPAN ou MD-ICON). De plus, la suggestion faite ailleurs sur le Web de modifier ma version d’Aria n’a pas aidé non plus. J'ai fini par trouver que le problème était que je chargeais jquery avant angular.js même si la doc d'aide de ng-infinate-scroll disait de charger jquery en premier. Quoi qu’il en soit, si j’essayais de charger jQuery après que angular ng-infinate-scroll cesserait de fonctionner. J'ai donc téléchargé la dernière version de ng-infinate-scroll (non stable) version 1.2.0 et maintenant je peux charger mon jquery après angular.js. disparu.

0
Helzgate

Mes éléments se sont également enveloppés. 

J'ai donc utilisé une astuce CSS simple pour cette solution:

.off{
   pointer-events:none;
}
0
ivahidmontazer