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

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

Related

Checkbox change event bubbles up to parent li element while using e.stopPropagation()

I've added a change event listener input[type='checkbox'] element, but even though I've included the e.stopPropagation() in my JS code, the event seems to bubble up to the click listener on the <li> element.
My HTML structure:
<li>
<label class="checkboxContainer">
<input type="checkbox">
<span class="checkmark"></span>
</label>
Lorem ipsum
</li>
It's important to note that that checkbox element doesn't take up any space. Rather, the label element is customized to create a custom checkbox. Adding the eventListener to the label has the same effect.
My JS code:
eventListener on checkbox change event
const checkbox = document.querySelector(".checkboxContainer input");
checkbox.addEventListener("change", e => {
e.stopPropagation();
//rest of code goes here
}):
eventListener on li click event
const li = document.querySelector('li');
li.addEventListener("click", e => {
//code goes here
});
See image below to get a sense of what I want to achieve
When I click on the blue area, the todo should fade out (the checkbox event). When I click on the text of the li, the edit screen should toggle. (the li click event)
Now when I click the blue area the li event get's triggered to. So the todo fades out and the edit screen shows up.
I would be very grateful for any suggestion how I can solve this problem!
EDIT: if you trigger the click event only on the text (which is all you need according to your example) it works:
const checkbox = document.querySelector(".checkboxContainer input");
const span = document.querySelector('.edit');
checkbox.addEventListener("change", e => {
console.log("only blue fade out")
});
span.addEventListener("click", e => {
console.log("change text")
});
<li>
<label class="checkboxContainer">
<input type="checkbox">
<span class="checkmark"></span>
</label>
<span class="edit">Lorem ipsum</span>
</li>
The problem your are facing is that you are clicking the event on li first.
As soon as you change the checkbox you are first clicking the li as this is the parent element.
const checkbox = document.querySelector(".checkboxContainer input");
checkbox.addEventListener("change", e => {
e.stopPropagation();
console.log("checkbox")
});
const li = document.querySelector('li');
li.addEventListener("click", e => {
console.log("li")
});
// Onchange Checkbox CONSOLE:
li
checkbox
// Onclick li CONSOLE:
li
I think this is because the event that you are stopping from propagation is not the same event that's being captured in the li event handler. There are two events here, changed and clicked. Just add click as well as change to your checkbox event listener and it should make it work:
const checkbox = document.querySelector(".checkboxContainer input");
checkbox.addEventListener("click", e => {
e.stopPropagation();
//rest of code goes here
}):
Although it would be problematic cause you're hiding your checkbox, so maybe set it up on your label.

Checking if my input box has been click or is 'in focus' pure js

I need to know if my input box is clicked so I can fire a script to do something, except none of the routes I'm attempting seem to do anything.
basic input box
<input type="text" id="search-stuff" placeholder="search"/>
Javascript
var inputBox = document.getElementById("search-stuff");
if (inputBox) {
inputBox.addEventListener('keyup',function () {
startSearch();
})
inputBox.addEventListener('onfocus',function() {
console.log('we clicked');
searchBoxClicked();
})
}
function searchBoxClicked() { console.log('we clicked it'); }
I need to know if the user clicks so I can clear some classes on previous elements before the user types something.
You nearly got it right. A few minor mistakes:
to listen to the focus event. onfocus isn't an event.
the keyup event is for listening when a keyboard button is released (following a keydown event). If you want to listen to a mouse click, use the click event.
var inputBox = document.getElementById("search-stuff");
if (inputBox) {
inputBox.addEventListener('click', function() {
startSearch();
});
inputBox.addEventListener('focus', function() {
searchBoxClicked();
});
}
function searchBoxClicked() {
console.log('focus');
}
function startSearch() {
console.log('click');
}
<input type="text" id="search-stuff" placeholder="search" />
Attach the event listener to your input instead of the form. Instead of getElementById you could iterate the children to find it or use a selector.
When using event listeners the event is click or focus not onclick or onfocus.
document.getElementById('search-stuff').addEventListener('focus',function() {
console.log('clicked it');
};
or
document.querySelector('#formName > input[type=text]').addEventListener('focus',function() {
console.log('clicked it');
});
document.querySelector('#formName > input[type=text]')
.addEventListener('focus',function() {
console.log('clicked on ' + this.name);
});
<form id="formName">
<input name="username" type="text" value="press here">
</form>

Pass Ctrl + Click Event to another element in Firefox

It's come to my understanding that Firefox will not pass Ctrl + Click from label, to target input field. I'm at a loss as to how I can achieve this behaviour. I have checkbox fields, which are hidden, and their respective labels that are styled uniquely and visible... When clicking the label, click registers, but control click does not.
I assume html as
<form>
<label for="home" id="lblhome">home</label>
<input type="checkbox" name="home" id="home"><br>
</form>
I want one thing if ctrl+cliked other thing if it's just click , the checkbox click code
$( "#home" ).click( function( e) {
if(e.ctrlKey)
alert('i just got ctrl+cliked');
else
alert('i just got clicked');
});
So now create handler for label check if it's ctrl+click if yes then create an jquery Event and change its ctrlKey property to true and trigger it on the checkbox.
$('#lblhome').click(
function(e){
if(e.ctrlKey) {
alert('ctrl+click on label');
//cancel the normal click
e.preventDefault();
e.stopPropagation();
e2 = jQuery.Event("click");
//e2.which = 50; // for other key combos like CTRL+A+click
e2.ctrlKey = true;
$( "#home").triggerHandler(e2);
$( "#home").prop("checked", !$( "#home").prop("checked"));//toggle checkbox state
}
});
DEMO : https://jsfiddle.net/35g41aaL/
This is kind of hacky, but it works: http://jsbin.com/dogelehena/edit?html,console,output
<input type="checkbox" id="cb">
<label for="cb">Label</label>
<script>
$(function () {
var cb = $('#cb');
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
$('label').click(function (e) {
if (e.ctrlKey && isFirefox) {
var checked = cb.prop('checked');
cb.prop('checked', !checked);
}
});
});
</script>
First, attaching the click event to the label and not the input. This way, the event is not blocked when ctrl is held down, but the checkbox state does not change. So in the callback, we check for e.ctrlKey, and if it's true, we change that checkbox's state manually.
Please note that I added the isFirefox check, because while this functionality fixes the behavior in Firefox, it breaks in other browsers.

Javascript click event firing twice, even with stopPropagation

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>

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.

Categories

Resources