jQuery Click wird beim Klicken auf die Beschriftung zweimal ausgelöst

114

Ich verwende jQuery, um benutzerdefinierte Optionsfelder zu erstellen, und ich habe ein Problem. Wenn Sie auf die Beschriftung klicken, die dem Radio zugeordnet ist, werden die Klickereignisse zweimal ausgelöst. Wenn ich nur auf das Radio selbst klicke, funktioniert dies einwandfrei (eigentlich ist es nicht das Radio, auf das ich klicke, sondern das Div, das die gesamte Eingabe und Beschriftung umschließt). Hier ist der Code:

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

Gibt es eine Lösung für dieses Verhalten?

Ton
quelle

Antworten:

130

Versuchen Sie hinzuzufügen:

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

auf .bind () oder .click (), je nachdem, was Sie sehen. Fügen Sie evtder Funktion auch den Parameter hinzu , zfunction(evt) {...

Jordanien
quelle
9
warum passiert das?
Chovy
8
Weil es verschachtelte Elemente gibt. Jedes Element in der Hierarchie sprudelt das Ereignis in die Luft.
Jordanien
7
Wenn Sie dies für ein Kontrollkästchen verwenden, wurde dies auch für mich benötigt, damit die Eingabe tatsächlich aktiviert werden kann:jQuery('input').prop('checked', true);
David Sinclair
6
return false;ist äquivalent zu `evt.stopPropagation (); evt.preventDefault (); ( in jQuery )
Basilikum
193

Ich habe versucht, die obige Lösung hinzuzufügen, indem ich Folgendes hinzufügte:

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

hat aber nicht funktioniert. Fügen Sie jedoch Folgendes hinzu:

evt.stopImmediatePropagation();

Problem gelöst! :) :)

N3da
quelle
6
Ich weiß nicht warum, aber was Sie gesagt haben, sieht nach Arbeit für meinen Code aus. Danke dir!
Shaosh
8
Perfekt. Das hat gut funktioniert! Obwohl evt.stopPropagation(); evt.preventDefault();nicht; 't
James111
1
Nur das funktionierte für mich, ich hatte die anderen Funktionen ohne Erfolg
ausprobiert
1
Diese Antwort hat mir das Leben gerettet! Danke: D
Bluetree
1
Das war die Antwort für mich!
Dyluck
73

Binden Sie das Klickereignis an die Eingabe und nicht an die Beschriftung. Wenn auf das Etikett geklickt wird, tritt das Ereignis weiterhin auf, da, wie Dustin erwähnt hat, ein Klick auf das Etikett einen Klick auf die Eingabe auslöst. Dadurch kann das Etikett seine normale Funktionalität beibehalten.

$('input').click();

Anstatt

$('label').click();
dougmacknz
quelle
10
Diese Lösung funktioniert auch, wenn Ihr Markup die Technik des Umwickelns der Eingabe mit Etiketten verwendet und nur meine
geistige Gesundheit
4
Sie sollten sich changesogar an das Optionsfeld binden, da der Text eines Etiketts anklickbar ist - sie klicken nicht immer auf das Optionsfeld selbst.
Chovy
2
Wenn Sie ein Bootstrapesque- label > inputSetup verwenden, ist dies DIE Antwort, keine Antwort. Das Hinzufügen evt.stopPropagation()oder Hinzufügen evt.preventDefault()zu Ihrem Code ist zwar effektiv, aber ein Hack, der vermieden werden sollte, wenn die richtige Lösung viel sauberer und effizienter ist.
elPastor
wow wow nur wow, ich habe fast ein paar Tage damit verbracht herauszufinden, endlich hat das geholfen und vielen Dank für die Erklärung
OpenSource-Entwickler
das hat meinen Tag gerettet!
gab06
11

Wenn Sie versuchen, einen äußeren Container als Klickelement zu verwenden, können Sie die Ereignisse auch auf natürliche Weise sprudeln lassen und in Ihrem Klick-Handler auf das erwartete Element testen. Dieses Szenario ist nützlich, wenn Sie versuchen, eine eindeutige Klickzone für ein Formular zu erstellen.

<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
quelle
1
Dies funktionierte für mich, da ich immer noch wollte, dass das Optionsfeld ausgewählt wurde. Ich wollte einfach nicht, dass der Event-Handler alles zweimal macht.
Chovy
1
Dadurch können keine Kinder ausgewählt werden. Übrigens wäre eine bessere Option, um die gleiche Funktionalität zu erreichenif (e.target == e.currentTarget) {}
Hühnersuppe
9

Um dies auf einfache Weise zu beheben, entfernen Sie das Attribut "for" auf dem Etikett. Ein Klick auf die Beschriftung löst auch einen Klick auf das zugehörige Element aus. (In Ihrem Fall wird Ihr Klickereignis zweimal ausgelöst.)

Viel Glück

Dustin
quelle
In der Tat die schnelle Lösung. Man könnte argumentieren, dass es mit dem Markup zu tun hat, aber ich würde dem entgegenwirken, dass der Zweck des Attributs "for" die Ereignisausbreitung ist. Wenn Sie also einen anderen Mechanismus (jquery) verwenden, sollten Sie "for" auf jeden Fall loswerden.
Chris Harrington
5

Normalerweise benutze ich diese Synthax

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

anstatt

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

Die beste Antwort ist in Kommentaren versteckt:

Sie sollten sich changesogar an das Optionsfeld binden, da der Text eines Etiketts anklickbar ist - sie klicken nicht immer auf das Optionsfeld selbst. - Chovy 12. Dezember 13 um 1:45 Uhr

Diese Geige zeigt , dass alle anderen Lösungen - stopPropagation, stopImmediatePropagation, preventDefault, return false- entweder nichts ändern oder die Checkbox / Radio Funktionalität zerstören). Es zeigt auch, dass dies ein Vanille-JavaScript-Problem ist, kein jQuery-Problem.

BEARBEITEN: Eine andere funktionierende Lösung, die ich gerade in einem anderen Thread gefunden habe, besteht darin, das onclickan die Eingabe und nicht an das Etikett zu binden . Geige aktualisiert .

WoodrowShigeru
quelle
2

Ich habe versucht, durch Hinzufügen von Lösung.

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

hat aber nicht funktioniert.

Beim Hinzufügen

evt.stopImmediatePropagation();

Problem gelöst! :) :)

Janki Moradiya
quelle
1

Das Problem mit e.preventDefault (); verhindert, dass das Klicken auf das Etikett das Optionsfeld aktiviert.

Eine bessere Lösung wäre, einfach eine Schnellprüfung "wird geprüft" wie folgt hinzuzufügen:

$("label").click(function(e){
  var rbtn = $(this).find("input");
  if(rbtn.is(':checked')){
  **All the code you want to have happen on click**
  }
)};
Yoshyosh
quelle
1
Eine noch prägnantere Lösung wäre die Verwendung von .mouseup anstelle von .click
yoshyosh am
1
Diese Lösung funktioniert nicht, wenn Ihr Code es einem Benutzer auch ermöglicht, die Auswahl eines Optionsfelds aufzuheben. Das Doppelfeuer verursacht in diesem Fall ernsthafte Probleme.
Johncl
1

Mein Problem ist ein bisschen anders, da das evt.stopPropagation();evt.preventDefault();bei mir nicht funktioniert, füge ich return false;am Ende einfach hinzu , dann funktioniert es.

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

In meinem Fall war das Problem, dass ich das Klickereignis in einer Funktion hatte und die Funktion zweimal ausgeführt wurde . Jede Ausführung der Funktion erzeugt ein neues Klickereignis. - Gesichtspalme -

Nach dem Verschieben des Klickereignisses außerhalb der Funktion funktionierte alles wie erwartet! :) :)

FalcoB
quelle
0

Versuchen Sie, Ihr Eingabe-Tag außerhalb des Trigger-Elements zu platzieren, da das Label-Tag den Klick emuliert, sodass Sie immer mehr als einen Aufruf haben.

Benjamin
quelle
0

Ich hatte das gleiche Problem, weil ich mein Radio wie folgt mit dem an radio_div angehängten Handler in das Label verschachtelt hatte. Durch Entfernen des verschachtelten Labels wurde das Problem behoben.

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

Das Etikett löst das zu prüfende Radio / Kontrollkästchen aus.

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

Es verhindert insbesondere das Label, dieses Verhalten auszulösen.

Kevin.B
quelle
0

Der Klick auf die Beschriftung mit dem Attribut for = "some-id" löst einen neuen Klick aus, jedoch nur, wenn das Ziel vorhanden ist und eine Eingabe ist. Ich konnte es mit e.preventDefault () oder ähnlichen Dingen nicht perfekt lösen, also habe ich es so gemacht:

Zum Beispiel, wenn Sie diese Struktur haben und ein Ereignis beim Klicken auf .some-class möchten

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

Was funktionierte war:

$(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
quelle