Canceling checkbox's click event prevents checking it? - javascript

I need to have a list of checkboxes with the condition that at least one of them must be checked.
The following code produces that effect.
document.querySelector('div').addEventListener('click', function(evt){
if( this.querySelectorAll('input:checked').length == 0 )
evt.preventDefault() ;
}) ;
<div>
<input type=checkbox checked>
<input type=checkbox>
<input type=checkbox>
</div>
That's fine, however, I don't understand why this code even works.
Firstly, I'm doing a .preventDefault() on the click event only after I've checked the condition that says there are no checkboxes checked. So canceling the click event at this point should make no difference.
Secondly, the code works even if you try to check the checkboxes using the keyboard, which is totally weird because I'm only canceling the click event.
Please explain why the code works the way it does.

While an input event listener is running, its effect on the element's state is actually already performed. If event.preventDefault() is called, this change is undone when the listener returns. This allows a checkbox event handler to test the new state of the checkbox, and allows the handler for a keyboard event on a text input to test the value that includes the new input.
The reason it works when you use the keyboard is that the click event is a high-level event that encompasses all the different ways to click on a checkbox: you can do it with the mouse, with they keyboard, with a touchscreen, etc. If you want to listen to a specific mode of clicking, you would have to use mousedown, keypress, etc.

Related

JQuery Click event loads before change event

I am using a simplified example to describe the issue I am facing.
I have the following HTML markup:
<input ng-model="something" style="margin-top:8px;"/>
And, I have two HTML buttons:
<button id='submit'>Save</button>
<button id='btnGetAnalyzerInput'>Generate Analyzer File </button>
I used jQuery's change event on my input (to track whether any changes have been made to the input - by maintaining a simple JS variable).
When the user clicks "Generate Analyzer file button", what I want to is this:
Look up the JS variable to find out whether any changes have been made.
If yes, then prompt the user to save changes (window.dialog)
However, I find that when the focus is still on the input element, and when the button is clicked, the click event runs before the OnChange event. In all other cases, it is the OnChange event which gets fired before the click event (and so my code works as expected).
Is there any way to ensure that for such a scenario, the click event runs after the onChange event?
I am using Google Chrome to test my application.
Note :
Both events work as expected - the OnChange event gets fired when the textbox loses focus.
I can't use the keypress event since I want to track changes.
You could have the click event call the same function as the OnChange event. Something like this:
function OnChange(){
//Do stuff for on change;
}
function ClickEvent(){
OnChange();
//continue with generate stuff
}
You you may need to set up and pass in arguments to the OnChange function, depending on how you are accessing the data you need. If you need more guidance, post more of your code.

Javascript Change and Click events don't both fire

I have a textarea or input, and I change the text, and then click a link or button (without tabbing off the input), only the change event fires, not the click event. I want them both to fire:
Enter some text and then click the link. We want f1 and f2 to appear !
<br />
<input onchange="f1();" value="input text" />
<br />
click me
function f1() {
alert("f1");
return true;
}
function f2() {
alert("f2");
return true;
}
http://jsfiddle.net/paull3876/vrcfherp/3/
I've tried it with jquery and normal js as above, and I've tried setTimeouts inside the functions, I've tried input and textarea, but whatever I do, only f1 fires.
UPDATE 1
So we've established that alerts kill any subsequent events - good. Fiddle
Now the challenge is, how to stack up these events in an object so each event can fire an alert(or confirm, or ...) and NOT block any future events. I'm just trying different ideas. More to come...
UPDATE 2
I wrote some code to stack up the functions and then execute them at the end. Its not beautiful (and it should be wrapped in a class) but it works:
http://jsfiddle.net/paull3876/vrcfherp/9/
This behavior is due to the blocking mode of alert.
Browser will trigger the events one after the another. In this case the priority must be
onChange event of <input>
onClick event of <a>
but when it first executes onChange of input, it encouters alert which blocks rest of the things, hence the event onClick of anchor tag is not triggered.
Check this if you change from alert to console.log
alert() is modal and because input loses focus on other element mousedown, there is no click event fired.
So start using console for debugging purpose.

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?.

Is this a core misunderstanding of the default click event on checkbox inputs, or flawed code?

When binding to a click event for a checkbox input, the checkbox is already toggled by the time my event handler runs and, more oddly, the toggle is reversed after my event handler runs if I specify event.preventDefault();
<input id="foo" type="checkbox"/>
function clicked(evt) {
alert(document.getElementById('foo').checked);
evt.preventDefault();
}
document.getElementById('foo').addEventListener('click',clicked);
[tested in chrome and firefox]
JSFiddle for that code
The alert will respond "true" (or the opposite state of the checkbox pre-click). After you dismiss the alert, the checkbox toggles back.
So, I guess the questions are,
What is the actual default event being prevented? Am I wrong in assuming my event handler should be running before the state is changed? Is there a way to legitimately intercept a checkbox click?
And why in the world is the preventDefault causing the re-toggling the checkbox?
The actual default event being prevented is the click event. The misunderstanding probably occurs because you are thinking of the event as firing after the actual click has been fully processed (i.e. the checkbox has been toggled) while in reality the event model stipulates that the handler fires while the event is being processed.
If it helps, another model I 've found useful in explaining how it all works is the database transaction model: think of your event handler as being invoked as part of a transaction, inside which the checkbox has already been toggled.
If you read the state of the checkbox, you will find it toggled (the "write" has been sent to the database).
However, you can still decide to rollback the transaction (in which case the write is undone and the checkbox is toggled back to its original value).

What events does an <input type="number" /> fire when its value is changed?

Just wondering whether anyone knows what events an HTML5 <input type="number" /> element fires when its up / down arrows are clicked:
I'm already using an onblur for when the focus leaves the input field.
change would be the event that is fired when the field's value changes.
I think the HTML5 event input would also fire.
I found that for jQuery the following code covered keyboard input, mousewheel changes and button clicks in Chrome, and also handled keyboard input in Firefox
$("input[type=number]").bind('keyup input', function(){
// handle event
});
I found that onkeyup and onchange covered everything in Chrome 19.
This handles direct value input, up down arrow keypress, clicking the buttons and scrolling the mousewheel.
onchange alone would be sufficient in Chrome, but other browsers that only render the field as a text box need the onkeyup binding, which works perfectly to read the new value.
Binding the mousewheel event separately was less successful. The event fired too early - before the field value was updated - and therefore always gave the field's previous value
The onchange event fires on blur but the oninput event fires as you type. Maybe you might want to put a timer on the oninput event and fire your onchange event when the user has stopped typing for a second?
There is a current bug in Edge preventing change or input from firing when using the arrow keys in a number input.

Categories