Why does html label for trigger callback on containing div twice? - javascript

I saw another question that asks something similar, but the difference is that they put the input element inside the label tag, whereas mine is outside:
<!DOCTYPE html>
<head>
<style>
div {
background-color: #AAA;
width: 100px;
}
</style>
</head>
<body>
<div id='foo' onClick="alert('clicked')">
<label for='foo-pie'>Pick Foo:</label>
<select id='foo-pie' name='wat[wat]'>
<option value='pie'>Foo</option>
</select>
</div>
</body>
</html>
If I click on the div, not on the label, the alert triggers once. If I click on the label, the alert triggers twice. Why is this? It seems to me like the alert should trigger once regardless. If I remove the for= it only triggers once, which suggests to me that there's some sort of event bubbling going on... JSFiddle

This is because label is an interactive element. When clicked it triggers an activation event on its associated element. This activation event usually causes a click event, so you get two clicks instead of one. One for the actual user click and one caused by the activation event.
http://www.w3.org/TR/html5/forms.html#the-label-element
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.
While the example from the docs shows an onClick for the input element, the event would bubble up so in your case your div gets the bubbled click event
Interactive-content doc
To prevent this you can prevent the default action, or use stopPropagation to stop the event from bubbling up.
Inline
<label onclick="return false;" for='foo-pie'>Pick Foo:</label>
in code
HTML
<label id="foolabel" for='foo-pie'>Pick Foo:</label>
JS
document.getElementById("foolabel").addEventListener(function(e){
e.preventDefault(); //Or
e.stopPropagation();
});
Example
<div id='foo' onClick="alert('clicked')">
<label onclick="event.stopPropagation();" for='foo-pie'>Pick Foo:</label>
<select id='foo-pie' name='wat[wat]'>
<option value='pie'>Foo</option>
</select>
</div>

My theory is that this is a feature called event propagation, and by defining your <div> with the onclick attribute it has somehow bound that event to the children as well.
Try throwing e.stopPropagation into your event callback and it should stop it from being called multiple times.
MDN Example

Related

Can't check radio button programmatically with preventDefault()

I have some elements (for example divs with class .label) with radio buttons inside of each. When user clicks this "labels" I programmatically set radio in it as selected. But if I use preventDefault() for click event, the radio didn't selected if user clicked exactly on radio.
Please help me to understand this strange behaviour. I know the solution, I know why preventDefault() on parent element disallows to check radio, but I want to understand, why click event on radio can disallow to set its state programmatically. You will see that click on radio button will say that radio is checked, but it's not.
$(function () {
$('.label').on('click', function(e) {
var $radio = $(this).find(':radio')
console.log('before prevent', `checked=${$radio.prop('checked')}`, `prevented=${e.isDefaultPrevented()}`);
e.preventDefault();
if (!$(this).hasClass('checked')) {
$('.checked').removeClass('checked');
$(this).addClass('checked');
}
$radio.prop('checked', true);
console.log('after prevent', `checked=${$radio.prop('checked')}`, `prevented=${e.isDefaultPrevented()}`);
setTimeout(function () {
console.log('after timeout', `checked=${$radio.prop('checked')}`);
}, 500);
});
$(':radio').on('click', function (e) {
console.log('click', `prevented=${e.isDefaultPrevented()}`);
});
});
.label {
padding: 10px;
margin-bottom: 10px;
border: 1px solid #000;
}
.label.checked {
background-color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="label">
Label 1 <input type="radio" name="radio" value="1">
</div>
<div class="label">
Label 2 <input type="radio" name="radio" value="2">
</div>
UPDATE. How do I see this situation:
User clicks on radio
Firstly event triggered on radio and input setted as checked.
Then event is bubbling up and triggered on .label
Calling preventDefault() sets up an internal cancelled flag.
div getting class '.checked' and radio setted as checked again, now programmatically.
Event bubbles on, but nothing happens any more.
Since the event was cancelled, the default action should not occur and the checkbox is reset to its previous state.
Am I right?
How do I see this situation (inspired by https://stackoverflow.com/a/15767580/11357125):
You click on the radio
It gets checked
The event is dispatched on the document root
Capture phase, nothing happens with your handlers
The event arrives at the <input>
…and begins to bubble
On the <div>, it is handled. Event listener calls the preventDefault method, setting an internal cancelled flag. <div> getting class '.checked' and radio setted as checked again, now programmatically.
Event bubbles on, but nothing happens any more.
Since the event was cancelled, the default action should not occur and the checkbox is reset to its previous state even after it was checked programmatically.
Using preventDefault() on parent elements prevent the original event to be fired, but does not stop propagation. Understanding hierarchy and event propagation is crucial.
You have part of the solution in your code snippet. If you comment out that particular line, the code works properly, like you would expect.
But if you use
e.stopPropagation();
it also works.
In order not to repeat information already on the web, I found a very similar case here (Why does preventDefault() on a parent element's click 'disable' a checkbox?) which may help you understand better event propagation and bubbling.
You can also find an even better explanation here (https://medium.freecodecamp.org/a-simplified-explanation-of-event-propagation-in-javascript-f9de7961a06e).
MDN documentation also rarely fails to impress (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault).
You just need to add e.stopPropagation() to return the default functionality for radio button again which is checked
The behaviour is that You have radio is a child to the div and you click listener is based on the parent div and when you preventDefault then the children inherit the prevention as well.
check that
If the button is pressed on one element and released on a different one, the event is fired on the most specific ancestor element that contained both.

Event is firing Two times

Hi I am binding onclick event to parent as well as child (same method). Event is getting fired two times How to avoid this?
<div id="sparentId" onclick="javascript:somemethod()" >
<button id="childId" onclick="javascript:somemethod()"></button>
</div>
Onclick of Div the event is getting fired two times.
Try:
<button id="childId" onclick="javascript:somemethod(event)"></button>
JS code
function somemethod(event){
event.stopPropagation();
}
The reason that the onclick event is showing twice, is because there is something called event bubbling in JavaScript.
Take a look at the following:
This image shows that, if the <img> was clicked, the event would "bubble" up to the <p> tag, then to the <div>, then to the rest of the document. If there was an onclick event on the <p> tag, and even if the <p> tag was not clicked (but the <img> was), the event would necessarily "bubble" all the way up the DOM, and would still continue when an event was fired on the <p> tag (in other words, if you also had an onclick event on the <div>, then that would fire as well.
So what you should do is this:
<div id="sparentId" onclick="javascript:somemethod()" >
<button id="childId"></button>
</div>
In other words, as I explained above, you wouldn't need the extra onclick event handler in your button anymore, because when your button is clicked, the event bubbles up to the parent div, which would fire that event.
If you want to use your original HTML code, that's fine as well - just add this:
function somemethod(evt) { // the method you run
// some code
evt.stopPropagation(); // stops bubbling
}
This effectively stops the event from bubbling up your DOM tree.
It is getting called twice because you are calling it twice, i.e. once in the div click and once in the button click. the button is already inside the div.
<div id="sparentId" onclick="javascript:somemethod()" >
<button id="childId" onclick="javascript:somemethod()"></button>
</div>
try
<div id="sparentId">
<button id="childId" onclick="javascript:somemethod()">Click Me</button>
</div>
use
event.stopImmediatePropagation()

jQuery HTML click event firing twice

I have a custom select menu button, and I have bound to the html click event to close it. But the event is firing twice.
http://jsfiddle.net/GnzBj/1/
$(function () {
$('html').click(function () {
console.log('html');
});
});
Any one know why/how to prevent it firing twice?
The reason why the event is triggered twice is because you have the whole UI inside a label.
<div ...>
<label for="xmod-form-51183d51afa3d" ... >
<select name="theme" id="xmod-form-51183d51afa3d" ...>
...
</select>
...
</label>
</div>
Clicking on the label will also trigger a click event on the form element it relates to.
If you remove the label element, it works as expected: http://jsfiddle.net/GnzBj/5/.
In case you need the label, let it contain as few as possible of the UI, but clicking on it will still trigger two events.

DOM/jQuery events propagation differences between input types

As we know, returning false from a DOM event handler will stop the propagation of the event. But I recently discovered that the behavior of different elements varies with respect to this event. For example, consider the following:
<div id="container">
<input type="checkbox" name="foo" value="1" /> Check me!
<br />
<select>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
<br />
<input type="text" size="30" />
</div>
The surrounding container has a click handler that returns false:
$('#container').click(function(e) {
$('#clicks').append('<span>clicked</span>');
return false;
});​
Within the div, I can still click on the text box and enter text, and can still click on the dropdown to change its value. But clicking on the checkbox does nothing (actually, that's not quite true - it checks the box, fires the handler, then unchecks it). jsfiddle here: http://jsfiddle.net/4ncaq/
This behavior is consistent across browsers so I assume it's by design. But what exactly is the rule that's at work here? How can I know how other kinds of elements might behave in this type of scenario?
What you are seeing is propagation, events 'bubble' up the DOM, so when you click your checkbox it is actually firing the click event on #container (as well as any events you might have bound to the input or other parent elements).
Using return false like this is ambiguous because it is doing 3 things at once, when you most likely only want it to do one. It prevents the default functionality, halts propagation and stops further execution.
It is better practice to be specific and use the methods from the event object to prevent the default action or stop bubbling.
event.preventDefault() will stop things like loading the href from an anchor click, stopping a checkbox from being checked etc.
event.stopPropagation() will cancel propagation, this is useful for situations like your example - http://jsfiddle.net/4ncaq/1/
The other problem with return false is that it may not be executed, a common mistake I see people make in jQuery is having a click event on an anchor with an $.ajax() request followed by return false to stop the browser from loading the linked page. In this scenario, if there is an error coming from ajax() (not a response error, a jQuery error - usually a misspelt param or something) it will never hit return false; and the browser will load the linked page. Using e.preventDefault() entirely removes this problem.
When you click an element, the event will continue propagating the event until some handler decides to cancel the propagation. In this case, when you click the checkbox, it will raise the event for the <input> first and then propagate to #container where you are stopping propagation.
If you want to cancel the propagation from input elements such as checkboxes or textareas you should bind to their click event and stop propagation at that point.
Edited
return false also cancels the default action for the original target element. Checkboxes, links, radio buttons are some of the elements where the default click action is cancelable. The default action for the click event in a checkbox toggles the value of the checkbox while there is no default click action for the select which means it does not get cancelled.
I've tried to find a list of default actions without luck but you can check the links at Is there a standard resource for the "default action" of HTML elements?.

blur does not fire on p tag

I am trying to use blur on td,p without any success. jQuery documentation says that
"Originally, this event was only applicable to form elements, such as
input. In recent browsers, the domain of the event has been extended
to include all element types."
So it should fire on p/td tags.
Here is my sample code:
<p>Test 2
</p>
<p>Test 1
</p>
<script>
$(document).ready(function () {
$("p").blur(function () {
alert('blur');
});
});
</script>
Note however that if I try to invoke blur programatically it is firing.
$("p").blur();
I am assuming that when I click on the first p and then click anywhere else the blur should fire...
Am I missing something here?
Blur is the opposite event of a focus event. A p tag cannot be focused unless it has a tabindex set so your p tags won't blur.
Add the tabindex attribute to your p tags and a blur will be fired after tabbing or clicking e.g.
<p tabindex="10">test 1</p>
<p tabindex="11">test 2</p>
focusout event: http://jsfiddle.net/b98Bs/
blur event: http://jsfiddle.net/b98Bs/1/
(If you click the p, remember to click away from it after to fire the event)
UPDATE:
I have included a second jsfiddle to show that blur, as well as focusout, events also fire with an alert displaying the text of the p element that is losing focus.
If you want the tabindex to follow the order of the elements in the document, set them both to tabindex="0": http://jsfiddle.net/b98Bs/2/
Blur event doesn't work with elements like p. It is usually used with input text. If you focus, you can trigger event 'focus', when focus is out, it is
a blur event. You can use blur event there. Blur event says the loss of focus.
In below example, when focus is out, color of input text changes to red. When you focus, color is yellow.
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#blurr").blur(function(){
$(this).css("color","yellow");
});
$("#blurr").focus(function(){
$("#blurr").css("color","red");
});
});
</script>
</head>
<body>
<input type="text" id="blurr" value="click Here"/>
</body>
</html>

Categories