Javascript click event firing twice, even with stopPropagation - javascript

I have a set of items like this:
<label for="Cadenza-1" class="cars">
<input type="checkbox" value="1" alt="Cadenza" id="Cadenza-1" name="vehicles[]">
<img src="img/bill_murray_173x173.jpg">
<span>Cadenza</span>
</label>
there's about 13 of them. I want to add a class to the label when clicked. However, the click event fires twice. right now I'm debugging the click event then I'll add the class:
var cars = document.getElementsByClassName('cars');
for(var c = 0;c < cars.length; c++){
cars[c].addEventListener("click", function(e){
selectVehicle(cars[c],e);
},false);
}
function selectVehicle(el,e) {
console.log(e);
e.stopPropagation();
}
The console.log fires twice.

Try adding preventDefault after your stopPropogation:
function selectVehicle(el,e) {
console.log(e);
e.stopPropagation();
e.preventDefault();
}
I believe it is best to place console.log(e) after the stopPropogation & preventDefault though. You will also then need to implement functionality to set the checkbox to checked since this would prevent that from happening.

When the <span> or the <img> receives a "click" event, that'll bubble up the DOM to the <label> element, and your event handler will be called. The <label> then triggers another "click" event on the <input> element, and that also bubbles up to the <label>.
You can check in the handler to see whether the <input> was clicked:
function selectVehicle(el,e) {
if (e.target.tagName !== "INPUT") return;
// do stuff
}
Alternatively, you could just add the "click" handler only to the <input> itself.
Now you're also going to notice that your code isn't working because you've hit a common problem with binding event handlers inside loops. The problem is that the variable c will have as its value the length of the cars list by the time the event handlers actually run. There are a few ways of dealing with that; one is to loop with forEach() instead of a for loop:
[].forEach.call(cars, function(car) {
car.addEventListener("click", function(e){
selectVehicle(car,e);
}, false);
});

You are adding the event listener to the label, you should add the event listener to the checkbox because the label behavior copy the same of the input assigned in for.
Please note that if you click just in the checkbox the callbacks works fine, this is because the event on the label is raised by the checkbox.
The right way to do that is to add the event listener only for the checkbox or adding prevent default in the setlectVehicle callback.

You are not required to preventDefault or stopPropagation, but just to add listner on the input element.
cars[c].children[0].addEventListener("click", function(e) {
Try this. It is working as expected.
Additionally, you are not required to use Id's with label's for if the label element encloses the required input/other elements
var cars = document.getElementsByClassName('cars');
for (var c = 0; c < cars.length; c++) {
cars[c].children[0].addEventListener("click", function(e) {
selectVehicle(cars[c], e);
}, false);
}
function selectVehicle(el, e) {
console.log(e);
}
<label class="cars">
<input type="checkbox" value="1" alt="Cadenza" name="vehicles[]">
<img src="img/bill_murray_173x173.jpg">
<span>Cadenza</span>
</label>
<label class="cars">
<input type="checkbox" value="1" alt="Cadenza" name="vehicles[]">
<img src="img/bill_murray_173x173.jpg">
<span>Cadenza 2</span>
</label>

Related

Firing change() event for input checkboxes doesn't trigger all event handlers

We have around twenty input checkboxes, and they're divided into six groups using CSS classes. We added an event handler for the change event on each of the six classes, and then we added a final event handler for the change event on the input element itself. It looks like this:
(HTML)
<input type="checkbox" name="cbox" class="A"> 1
<input type="checkbox" name="cbox" class="A"> 2
<input type="checkbox" name="cbox" class="B"> 3
<input type="checkbox" name="cbox" class="B"> 4
// ...
// checkboxes for classes C, D, E
// ...
<input type="checkbox" name="cbox" class="F"> 20
(jQuery)
$('.A').change(function() {
// doesn't get called
});
$('.B').change(function() {
// doesn't get called
});
$('.C').change(function() {
// doesn't get called
});
$('.D').change(function() {
// doesn't get called
});
$('.E').change(function() {
// doesn't get called
});
$('.F').change(function() {
// doesn't get called
});
$('input[name="cbox"]').change(function() {
// this one gets called
});
$('input[name="cbox"]').change();
When I finally call $('input[name="cbox"]').change(), the only event handler that gets called is the last one, i.e.
$('input[name="cbox"]').change(function() {
// this one gets called
});
All the other event handlers that I added (for classes A, B, C, D, E and F) don't get called. Why is this? Shouldn't $('input[name="cbox"]').change() trigger all of the event handlers, and not just the one?
Try this,
$('input.A').change(function(event) {
alert($(this).val());
});
jsfiddle
I dont know why its not working, Working to me i posted fiddle for sandbox
let classnamelist = ['a','b','c','d','e','f','g','l','m','n','o','p','q','r','s','y',]
for(let i = 0; i<classnamelist.length; i++){
let el = $('<input>').attr({
'type':'checkbox',
'name':'cbox'
}).addClass(classnamelist[i]);
$('body').append(el);
$('.'+classnamelist[i]).change(function(){
alert($(this).is(':checked'))
})
}
$('input[name=cbox]').change();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
If you're adding the checkboxes dynamically you should try :
$(document).on('change', '.A', function() {
// do stuff
});

Event handling issue with label and input inside it [duplicate]

window.onload = function(){
var wow = document.getElementById("wow");
wow.onclick = function(){
alert("hi");
}
}
<label id="wow"><input type="checkbox" name="checkbox" value="value">Text</label>
This is my code, when I clicked on "Text" it will alert hi twice but when I clicked on the box, the onclick element will only trigger once, why?
When you click on the label, it triggers the click handler, and you get an alert.
But clicking on a label also automatically sends a click event to the associated input element, so this is treated as a click on the checkbox. Then event bubbling causes that click event to be triggered on the containing element, which is the label, so your handler is run again.
If you change your HTML to this, you won't get the double alert:
<input id="wowcb" type="checkbox" name="checkbox" value="value">
<label id="wow" for="wowcb">Text</label>
The label is now associated with the checkbox using the for attribute instead of wrapping around it.
DEMO
If your intention is to respond only to clicks on the label and not on the checkbox, you can look at the event.target property. It references the element that called the listener so that if the click wasn't on that element, don't to the action:
window.onload = function(){
var el = document.getElementById('wow');
el.addEventListener('click', function(event) {
if (this === event.target) {
/* click was on label */
alert('click was on label');
} else {
/* click was on checkbox */
event.stopPropagation();
return false;
}
}, false);
}
If, on the other hand, you want to only respond to clicks on the checkbox (where a click on the label also produces a click on the checkbox), then do the reverse. Do nothing for clicks on the label and let ones from the checkbox through:
window.onload = function(){
var el = document.getElementById('foolabel');
el.addEventListener('click', function(event) {
if (this === event.target) {
/* click was on label */
event.stopPropagation();
return false;
} else {
/*
** click is from checkbox, initiated by click on label
** or checkbox
*/
alert('click from checkbox');
}
}, false);
}
This version seems to have the most natural behaviour. However, changing the markup so that the label no longer wraps the checkbox will mean the listener is not called.
Event bubble.
The checkbox is the child node of the label. You click the checkbox. Event bubble to the label. Then alert pop up twice.
To prevent alert pop up twice when you click the checkbox. You can change you onclick function into this:
wow.onclick = function(e){
alert('hi');
stopBubble(e);
}
function stopBubble(e)
{
if (e && e.stopPropagation)
e.stopPropagation()
else
window.event.cancelBubble=true
}
Hope this can work for you.
The Label tag will be associated with the input tag inside it. So when you click the label, it will also trigger a click event for the input, then bubble to the label itself.
See this:
document.getElementById("winput").addEventListener('click', function(event){
alert('input click');
//stop bubble
event.stopPropagation();
}, false);
http://jsfiddle.net/96vPP/
Rammed headfirst into this gotcha again and decided to prove what it did to myself, hopefully helping me remember. To help future people to whom the above might not be clear enough, here the example I made.
https://jsfiddle.net/ashes/0bcauenm/
$(".someBigContainer").on("click", "input[type=checkbox]", ()=> {
$output.val("Clicked: checkbox\n" + $output.val());
});
edit: added the link with a snipped of the code.
This is probably the simplest answer. Just add a span around your text and stop event propagation.
window.onload = function(){
var wow = document.getElementById("wow");
wow.onclick = function(){
alert("hi");
}
}
<label id="wow">
<input type="checkbox" name="checkbox" value="value">
<span onclick="event.stopPropagation()">Text</span>
</label>
Or without inline-javaScript
window.onload = function(){
var wow = document.getElementById("wow");
wow.onclick = function(){
alert("hi");
}
var span = document.getElementsByTagName("span")[0];
span.onclick = function(event){
event.stopPropagation();
}
}
<label id="wow">
<input type="checkbox" name="checkbox" value="value">
<span>Text</span>
</label>
For those using React and Material UI - I've encountered this issue on one of my forms. event.preventDefault() is stopping the event from bubbling as described by the accepted answer.
<Button
onClick={(event) => {
event.preventDefault()
this.handleCardClick(planName)
}}
>
<FormControl component="fieldset">
<RadioGroup
row
value={selectedPlan}
>
<FormControlLabel
control={<Radio color="secondary" />}
label={label}
/>
</RadioGroup>
</FormControl>
</Button>
Use change to listen for events instead of click

How to apply validation to cloned elements in Jquery?

There is a textbox with label; having validation of isnumeric.
Money: <input type="text" id="dollar" name="dollar" data-require-numeric="true" value="">
//Textbox with id dollar0
At run time, I have created clone of above; by clicking on button named add; and this created another textbox with different id and other attributes same; say.
Money: <input type="text" id="dollar1" name="dollar1" data-require-numeric="true" value="">
//Cloned textbox; cloned by clicking on a button named clone
On both textboxes data-require-numeric is true.
Issue: For default textbox the JQuery validation is getting executed. But for new clone; JQuery is not running.
Following is jquery:
var economy= {
init: function () {
$('input[type="text"][data-require-numeric]').on("change keyup paste", function () {
check isnumeric; if yes then border red
});
}};
$(economy.init);
How to resolve this?
Try this : You need to register click event handler using .on() in following way where registering the click handler for document which will delegate the event to 'input[type="text"][data-require-numeric]'. This way you can handle events for dynamically added elements.
var economy= {
init: function () {
$(document).on("change keyup paste",'input[type="text"][data-require-numeric]',
function () {
check isnumeric; if yes then border red
});
}};
$(economy.init);
to bind change event on dynamic dom elements . use class reference instead of id . And bind the event to its parent like,
$(document).ready(function(){
$(".parent").on("keyup",".dynamicdom",function(e){
value = $(e.target).val()
//then do your validation here. and you can attach multiple events to it
})
})
<div class="parent">
<input type="text" class="dynamicdom" >
<input type="text" class="dynamicdom" >
</div>

jquery stopPropogation() not working as expected

here is my html :
<span class="checkbox checked replacement" tabindex="0">
<span class="check-knob"></span>
<input type="checkbox" name="data[InfoPagesClient][3][info_page_id]" value="25" checked="checked" class="">
</span>
<label for="InfoPagesClient3InfoPageId" class="label">asdasd</label>
now I want to show hide this pencil box on checkbox click event..
javascript :
$("p.checkbox-group span.checkbox").on('click', function(){
if($(this).hasClass('checked')) {
$(imgId).hide();
} else {
console.log('aaaaaaaaaaa');
$(imgId).show();
}
});
$("label.label").on('click', function(e) {
if ($(this).siblings('span.checkbox').hasClass('checked')) {
$(imgId).hide();
} else {
$(imgId).show();
}
e.stopImmediatePropagation();
});
clikcing on label it is going to span click event and prints console value... I tried using e.stopPropogation() and stopImmediatePropogation().. but ti is not working..
any idea ??
e.stopPropogation() or e.stopImmediatePropogation() will prevent the event from bubbling up, but will not stop the event immediately.
You can use e.preventDefault() along with e.stopPropogation(). e.preventDefault() will prevent the default event from occurring. You can check with the following change in your code.
$("p.checkbox-group span.checkbox").on('click', function(e){
e.preventDefault();
e.stopPropagation();
if($(this).hasClass('checked')) {
$(imgId).hide();
} else {
console.log('aaaaaaaaaaa');
$(imgId).show();
}
});
$("label.label").on('click', function(e) {
e.preventDefault();
e.stopPropagation();
if ($(this).siblings('span.checkbox').hasClass('checked')) {
$(imgId).hide();
} else {
$(imgId).show();
}
});
When you use label with for, browser will automatically click the associated control which triggers the "click" event. That is another event triggered later, in your case when you use e.stopImmediatePropagation();, it just stops the current "click" event and has no effect on the event of the associated control fired after that
To fix your issue, try removing for
Use this:
<label class="label">asdasd</label>
Instead of:
<label for="InfoPagesClient3InfoPageId" class="label">asdasd</label>
If you add the id attribute to your checkbox, then the label will work. Then you can simplify your code as follows:
$(function () {
$("p.checkbox-group input[type=checkbox]").on('change', function () {
if (this.checked) {
$(this).parent().addClass('checked').siblings('a.edit-content').hide();
} else {
$(this).parent().removeClass('checked').siblings('a.edit-content').show();
}
});
});
http://jsfiddle.net/gFXcm/2/
mmmh, isn't it a feature instead of a bug ? shouldn't the click on the label trigger the same action as the click on the "checkbox" ? That's precisely why the for attribute is used I guess.

How to stop an event for raising

I have two fields, one textbox and one div element.
I have an onblur event for textbox, and an onclick event for div element.
When I click the div element the textbox's onblur event occurs and not the onclick event of div element. How to suppress the onblur event when I click on the div element?
try to use a semafore.
if the click event is triggered set the semafore to true and check the value in the blur event call. if true - do not perform the action.
after successfully performing the click event reset the semafore to false.
I'm not sure what you are trying to do and is it right way or not.
But this will solve your problem by using flag named "f":
<head>
<script type="text/javascript">
var f = 0;
function f1() {
if (f == 0)
alert('blur');
}
function f2() {
alert('click');
}
function f3() {
f = 1;
}
function f4() {
f = 0;
}
</script>
</head>
<body>
<input type="text" onblur="f1();" />
<div onclick="f2();" onmouseover="f3();" onmouseout="f4();">
Click Me!
</div>
</body>
Good luck!

Categories

Resources