Parent click handler is always executing first - javascript

I have a simple list:
<li onClick={handleRowClick}>
<input type="checkbox" onClick={handleCheckboxClick} />
...
</li>
When I click in any place on the li (except its checkbox child), handleRowClick executes.
When I click on the checkbox, it always executes in this order:
row
row
checkbox
Here's what I've tried
using onClickCapture on each: checkbox, li, and both
calling e.stopPropagation() on each: checkbox, li, and both
calling e.nativeEvent.stopPropagation() on each: checkbox, li, and both
calling e.nativeEvent.stopImmediatePropagation() on each: checkbox, li, and both
calling e.nativeEvent.preventDefault() on each: checkbox, li, and both
With the code above, at some point the order became
row
checkbox
But the row is always executed BEFORE the checkbox, so I can't see how make them not overlap.
Also worth mentioning that both handlers have logic, that need to happen, so making the row never execute any code is not an option.
This seems like a trivial thing, like I should know the answer to this. What am I missing?
Edit
Here's the minimal reproducible example: https://codesandbox.io/s/shy-shape-u1me4?file=/src/App.js
In the example above I stripped a lot of things like CSS classes. While doing that, I realised that what I'm actually clicking is a label for the checkbox, and the checkbox is "hidden" in my app.
I solved the problem by moving the event handler to the label instead of the checkbox, and running:
e.stopPropagation() to prevent propagation to the parent li.
e.preventDefault() to prevent the label to trigger the checkboxes o'clock, which would also cause the event to propagate to the li above.

The problem seem to be that my UI library hides the actual input, and the visible area where I was clicking was actually the label. These 2 elements are siblings related by the id and htmlFor attributes respectively.
As I was attaching the event handler on the input, but the label was the one actually being clicked, the propagation was not properly being stopped.
I solved the problem by moving the event handler to the label instead of the checkbox, and running:
e.stopPropagation() to prevent propagation to the parent li.
e.preventDefault() to prevent the label to trigger the checkboxes o'clock, which would also cause the event to propagate to the li above.
<li onClick={handleRowClick}>
<input id="c-1" type="checkbox" />
<label htmlFor="c-1" onClick={handleCheckboxClick}>Foo</label>
...
</li>
const handleCheckboxClick = () => {
e.stopPropagation()
e.preventDefault()
}

Related

Click event handler on a div with label get triggered twice [duplicate]

In the following snippet, why does divClicked() trigger twice when the <label> is clicked, but only once when <input> is clicked?
function divClicked(_index) {
console.log("div click event");
}
function inputClicked(_index) {
console.log("input click event");
}
<div class="option" onclick="divClicked(1)">
<input id="1_1" name="group_1" type="radio" value="1" onclick="inputClicked(1)" />
<label for="1_1">label</label>
</div>
Note: I want to know why this happens, not a "quick fix" like: put onclick() on label.
This happens because of what the HTML spec describes at 4.10.4:
For example, on platforms where clicking a checkbox label checks the
checkbox, clicking the label in the following snippet could trigger
the user agent to run synthetic click activation steps on the input
element, as if the element itself had been triggered by the user:
<label><input type=checkbox name=lost> Lost</label>
On other platforms, the behavior might be just to focus the control,
or do nothing.
This means that when a <label> is clicked, the browser creates a second "synthetic" click event on the associated <input> element, in order to toggle its state.
The reason divClicked is triggered twice, is because the first event which comes from the <label> bubbles up to the <div>, and also the second, synthetic click event bubbles up to the <div>.
This is usually be cause of the bubbling principle of click event:
When an event happens on an element, it runs on it, its associated elements,its parent and other ancestors.
Now, The relation is when you click on label there a are two events which bubbles up here:
1) Click on div (which you expect)
2) Click on input (which is also expected)
2.1) When click on input is triggered then a click on div is also triggered again here
You can confirm this behavior by using event.bubbles prop.
EDIT:
The reason for the connection between label and input: (I know this is absolutely not required, as it's present all over the place yet)
A <label> can be associated with a control either by placing the control element inside the <label> element, or by using the for attribute. Such a control is called the labeled control of the label element. One input can be associated with multiple labels.
Taken from: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
Which means placing for on label referencing id of an input element will stimulate the behavior as if the element is inside the label. This would bubble a event on input onto label like any event on child to parent
At some moments, check also if the javascript file asset isn't loaded twice .... it shouldn't happen, but you never know.

Javascript click event triggers twice

In the following snippet, why does divClicked() trigger twice when the <label> is clicked, but only once when <input> is clicked?
function divClicked(_index) {
console.log("div click event");
}
function inputClicked(_index) {
console.log("input click event");
}
<div class="option" onclick="divClicked(1)">
<input id="1_1" name="group_1" type="radio" value="1" onclick="inputClicked(1)" />
<label for="1_1">label</label>
</div>
Note: I want to know why this happens, not a "quick fix" like: put onclick() on label.
This happens because of what the HTML spec describes at 4.10.4:
For example, on platforms where clicking a checkbox label checks the
checkbox, clicking the label in the following snippet could trigger
the user agent to run synthetic click activation steps on the input
element, as if the element itself had been triggered by the user:
<label><input type=checkbox name=lost> Lost</label>
On other platforms, the behavior might be just to focus the control,
or do nothing.
This means that when a <label> is clicked, the browser creates a second "synthetic" click event on the associated <input> element, in order to toggle its state.
The reason divClicked is triggered twice, is because the first event which comes from the <label> bubbles up to the <div>, and also the second, synthetic click event bubbles up to the <div>.
This is usually be cause of the bubbling principle of click event:
When an event happens on an element, it runs on it, its associated elements,its parent and other ancestors.
Now, The relation is when you click on label there a are two events which bubbles up here:
1) Click on div (which you expect)
2) Click on input (which is also expected)
2.1) When click on input is triggered then a click on div is also triggered again here
You can confirm this behavior by using event.bubbles prop.
EDIT:
The reason for the connection between label and input: (I know this is absolutely not required, as it's present all over the place yet)
A <label> can be associated with a control either by placing the control element inside the <label> element, or by using the for attribute. Such a control is called the labeled control of the label element. One input can be associated with multiple labels.
Taken from: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
Which means placing for on label referencing id of an input element will stimulate the behavior as if the element is inside the label. This would bubble a event on input onto label like any event on child to parent
At some moments, check also if the javascript file asset isn't loaded twice .... it shouldn't happen, but you never know.

How to call onkeydown/onfocus to a list tag?

I have a list, and within this list im trying to call a function when keyisdown/onfocus/etc? This wont trigger the function:
<li onkeydown="myFunction()"></li>
The normal onclick works fine(calling the function when the "click is going up") and will trigger the function:
<li onclick="andrafarg()"></li>
So my question is: How can i call this function when the list is clicked immediately down?
From the jQuery docs:
The keydown event is sent to an element when the user first presses a
key on the keyboard. It can be attached to any element, but the event
is only sent to the element that has the focus. Focusable elements can
vary between browsers, but form elements can always get focus so are
reasonable candidates for this event type.
So as li elements can't get focused, the keydown-event will never be triggered.
For a reference for focusable elements, you might have a look at this answer.
But because of the event-bubbling, an input-field inside the list will trigger the keydown-event, if the event-listener is registered on the input itself or a parent-element.
Example:
HTML
<ul>
<li><input type="text" /></li>
</ul>
Javascript
$('ul > li').on('keydown', function(){
alert('Event triggered');
});
Demo

knockoutjs checkbox and click on parent td

I have a table with a row. The row has a TD(with a checkbox in it) and on the TD I have a click function. So that when the TD is clicked the checkbox will get checked/unchecked.
It works fine when I click on the TD , but when clicking on the checkbox the (visual) value of the checkbox does not change( It does not get checked/unchecked )
The wanted situation is:
When I click the checkbox, the (visual) value of the checkbox changes and I can call a function.( for example to make an AJAX call )
When I click on the TD, the (visual) value of the checkbox changes and I can call a function. ( for example to make an AJAX call )
How can we achieve this?
Sample Code
The problem is that the click handler for the TD fires also when you click the checkbox, which means the checkbox gets changed by both the default click handler for the checkbox and your custom click handler for the TD (they counteract each other). The solution is to prevent clicks on the checkbox from bubbling to the TD. You can do this in Knockout with this binding: click:function(){return true}, clickBubble:false.
Here it is in action: http://jsfiddle.net/mbest/Eatdh/12/
I do think, however, that using a label is a better approach (see my other answer).
To avoid the click event issues, use the label element to make a larger area clickable. Here I've made the label a block element so it takes up the whole td:
<td>
<label style="display: block">
<input type="checkbox" data-bind="checked: checkBox" />
</label>
</td>
See http://jsfiddle.net/mbest/LsxSh/
Td Event seems to be overriding the the input's check click event
clicking the check box invokes the click hander code for the td:
self.checkBox(!self.checkBox());
this removes the check.
This isn't quite DRY, but its quick and functional: fiddle
<td data-bind="click:tdClick">
<input type="checkbox" data-bind="checked: checkBox, click:tdClick" />
</td>

Bind JavaScript jQuery click event on label AND on checkbox simultaneously

I'm using labels for my form, like this :
<label for="foo" id="bar">Label</label>
<input type="checkbox" id="foo" />
I want to hide an element when the user uncheck the box, and show it otherwise.
The problem is, if I bind the click event to "foo", it'll only works when the user clicks on the checkbox itself and not on the label. Therefore, do I also need to bind a click event on the label ? Or should I enclose both elements within a span ?
My HTML already contains 2344 elements, so I'd like to do it without adding anything, and without doubling the JavaScript code or the selector, if possible.
Instead of binding with the click() event, you should bind using the change() event, then however this change is triggered the outcome will be the same:
$('#foo').change(
function(){
// do whatever
});
References:
change().
The change event should fire for the input whether the label or input is clicked:
$("#foo").change(function () { ... });
Example http://jsfiddle.net/andrewwhitaker/6LMXW/

Categories