jQuery Click uruchamia się dwukrotnie po kliknięciu etykiety

114

Używam jQuery do tworzenia niestandardowych przycisków opcji i mam problem. Po kliknięciu etykiety, która jest związana z radiem, zdarzenia kliknięcia są uruchamiane dwukrotnie, jeśli kliknę tylko na samo radio, działa dobrze (właściwie to nie radio, które klikam, ale div, który otacza całe wejście i etykietę). Oto kod:

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>

Czy jest jakieś rozwiązanie tego zachowania?

ton
źródło

Odpowiedzi:

130

Spróbuj dodać:

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

do .bind () lub .click (), w zależności od tego, co widzisz. Dodaj również parametr evtdo funkcji, na przykładfunction(evt) {...

Jordania
źródło
9
dlaczego to się dzieje?
chovy
8
Ponieważ istnieją elementy zagnieżdżone. Każda pozycja w hierarchii będzie oznaczać wydarzenie.
Jordan,
7
Jeśli używasz tego jako pola wyboru, było to również potrzebne, aby umożliwić faktyczne sprawdzenie wejścia:jQuery('input').prop('checked', true);
David Sinclair
6
return false;jest równoważne z `evt.stopPropagation (); evt.preventDefault (); ( w jQuery )
bazylia
193

Próbowałem dodać powyższe rozwiązanie dodając:

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

ale nie zadziałało. Jednak dodając to:

evt.stopImmediatePropagation();

Rozwiązać problem! :)

N3da
źródło
6
Nie wiem dlaczego, ale to, co powiedziałeś, wygląda na pracę dla mojego kodu. Dziękuję Ci!
shaosh,
8
Idealny. To działało dobrze! Chociaż nie evt.stopPropagation(); evt.preventDefault();; 't
James111
1
tylko to działało dla mnie, próbowałem innych funkcji bez powodzenia
gardarvalur
1
Ta odpowiedź uratowała mi życie! Dzięki: D
Bluetree
1
To była odpowiedź dla mnie!
Dyluck
73

Powiąż zdarzenie kliknięcia z wejściem, a nie z etykietą. Po kliknięciu etykiety - zdarzenie będzie nadal występować, ponieważ, jak wspomniał Dustin, kliknięcie etykiety powoduje kliknięcie wejścia. Pozwoli to etykiecie zachować normalną funkcjonalność.

$('input').click();

Zamiast

$('label').click();
dougmacknz
źródło
10
To rozwiązanie działa również, jeśli twój znacznik używa techniki zawijania etykiet i właśnie oszczędził mój rozsądek: o)
Whelkaholism
4
w rzeczywistości powinieneś połączyć się changenawet z przyciskiem opcji, ponieważ tekst etykiety jest klikalny - nie zawsze klikają sam przycisk opcji.
chovy
2
Jeśli używasz label > inputkonfiguracji Bootstrapesque , jest to odpowiedź, a nie odpowiedź. Dodanie evt.stopPropagation()lub evt.preventDefault()do kodu, choć skuteczne, to hack, którego należy unikać, gdy właściwe rozwiązanie jest znacznie czystsze i wydajniejsze.
elPastor
wow wow po prostu wow, spędziłem prawie kilka dni na zastanawianiu się, w końcu to pomogło i bardzo dziękuję za wyjaśnienie
opensource-developer
to uratowało mi dzień!
gab06
11

Jeśli próbujesz użyć zewnętrznego kontenera jako elementu kliknięcia, możesz również pozwolić zdarzeniom w naturalny sposób bąbelkować i przetestować pod kątem oczekiwanego elementu w module obsługi kliknięcia. Ten scenariusz jest przydatny, jeśli próbujesz stylizować unikalną strefę kliknięcia dla formularza.

<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
źródło
1
To zadziałało dla mnie, ponieważ nadal chciałem wybrać przycisk opcji. Po prostu nie chciałem, aby program obsługi zdarzeń robił wszystko dwa razy.
chovy
1
To nie pozwoli na wybór dzieci. BTW, lepszym rozwiązaniem, aby osiągnąć tę samą funkcjonalność byłabyif (e.target == e.currentTarget) {}
Chicken Soup
9

Aby rozwiązać ten problem w łatwy sposób, usuń atrybut „for” z etykiety. Kliknięcie etykiety spowoduje również kliknięcie skojarzonego elementu. (co w Twoim przypadku powoduje dwukrotne uruchomienie zdarzenia kliknięcia).

Powodzenia

Dustin
źródło
Rzeczywiście szybkie rozwiązanie. Ktoś mógłby argumentować, że miesza ze znacznikami, ale sprzeciwiłbym się temu, że celem atrybutu „for” jest propagacja zdarzenia. Więc jeśli używasz innego mechanizmu (jquery), to zdecydowanie pozbądź się "for".
Chris Harrington
5

Zwykle używam tej składni

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

zamiast

.click(function () { console.log('click'); })
Dalibor
źródło
5

Najlepsza odpowiedź jest ukryta w komentarzach:

w rzeczywistości powinieneś połączyć się changenawet z przyciskiem opcji, ponieważ tekst etykiety jest klikalny - nie zawsze klikają sam przycisk opcji. - chovy 12 grudnia 2013 o 1:45

To skrzypce ilustruje, że wszystkie inne rozwiązania - stopPropagation, stopImmediatePropagation, preventDefault,return false - albo zmiana nic lub zniszczyć pole wyboru / funkcjonalność radia). Pokazuje również, że jest to zwykły problem JavaScript, a nie problem jQuery.

EDYCJA: Innym działającym rozwiązaniem, które właśnie znalazłem w innym wątku, jest powiązanie plikuonclick danymi wejściowymi, a nie etykietą. Zaktualizowane skrzypce .

WoodrowShigeru
źródło
2

Próbowałem, dodając rozwiązanie.

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

ale nie zadziałało.

Poprzez dodanie

evt.stopImmediatePropagation();

Rozwiązać problem! :)

Janki Moradiya
źródło
1

Problem z e.preventDefault (); czy powstrzymuje kliknięcie etykiety przed sprawdzeniem przycisku opcji.

Lepszym rozwiązaniem byłoby po prostu dodanie szybkiego sprawdzenia „jest zaznaczone” w następujący sposób:

$("label").click(function(e){
  var rbtn = $(this).find("input");
  if(rbtn.is(':checked')){
  **All the code you want to have happen on click**
  }
)};
yoshyosh
źródło
1
Jeszcze bardziej zwięzłym rozwiązaniem byłoby po prostu użycie .mouseup zamiast .click
yoshyosh
1
To rozwiązanie nie działa, jeśli Twój kod ma również pozwolić użytkownikowi na odznaczenie przycisku opcji. Podwójny pożar powoduje w tym przypadku poważne kłopoty.
Johncl,
1

Mój problem jest trochę inny, bo u evt.stopPropagation();evt.preventDefault();mnie nie działa, po prostu return false;na koniec dodaję i działa.

$("#addressDiv").on("click", ".goEditAddress", function(event) {
    alert("halo");
    return false;
});
GMsoF
źródło
1

W moim przypadku problem polegał na tym, że miałem zdarzenie click w funkcji i funkcja była wykonywana dwukrotnie ... każde wykonanie funkcji tworzy nowe zdarzenie kliknięcia. - facepalm -

po przeniesieniu zdarzenia click poza funkcję wszystko działało zgodnie z oczekiwaniami! :)

FalcoB
źródło
0

Spróbuj umieścić swój tag wejściowy poza elementem wyzwalającym, ponieważ tag etykiety emuluje kliknięcie, więc zawsze będziesz mieć więcej niż jedno wywołanie.

Benzoes
źródło
0

Miałem ten sam problem, ponieważ umieściłem moje radio wewnątrz etykiety w ten sposób z programem obsługi podłączonym do radio_div. Usunięcie zagnieżdżonej etykiety rozwiązało problem.

<div id="radio_div">
    <label>
       <input type="radio" class="community_radio" name="community_radio" value="existing">
           Add To Existing Community
    </label>
</div>
shaw2thefloor
źródło
0

Etykieta wyzwala zaznaczenie radia / pola wyboru.

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

Zapobiega to, zwłaszcza etykiecie, aby wywołać to zachowanie.

Kevin B.
źródło
0

Kliknięcie etykiety z atrybutem for = "some-id" wyzwala nowe kliknięcie, ale tylko wtedy, gdy cel istnieje i jest wejściem. Byłem nie stanie rozwiązać go doskonale z e.preventDefault () lub rzeczy tak więc zrobiłem to tak:

Na przykład, jeśli masz taką strukturę i chcesz zdarzenie po kliknięciu .some-class

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

Co zadziałało:

$(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
źródło