Click vs Input vs Change for checkboxes? - javascript

I have a form. I want to only allow form submit if the user checks precisely 4 out of 8 available checkboxes; and once the user checks 4 checkboxes I want to disable the remaining unchecked ones.
Should I add a hook to the click event? Or maybe the input event? Or perhaps the change event?
I'm overwhelemed by the amount of events that seem to duplicate each other's functionality.
I'm also confused by the documentation.
MDN docs about input:
For <input> elements with type=checkbox or type=radio, the input event should fire whenever a user toggles the control, per the HTML5 specification. However, historically this has not always been the case. Check compatibility, or use the change event instead for elements of these types.
MDN docs about change:
Unlike the input event, the change event is not necessarily fired for each alteration to an element's value.
And below:
Depending on the kind of element being changed and the way the user interacts with the element, the change event fires at a different moment:
When the element is :checked (by clicking or using the keyboard) for <input type="radio"> and <input type="checkbox">;
MDN docs about click:
An element receives a click event when a pointing device button (such as a mouse's primary mouse button) is both pressed and released while the pointer is located inside the element.
Practice:
The below JS fiddle seems to hint that all 3 events are equivalent. Clicking the checkbox, clicking the label, focusing the checkbox and pressing space on keyboard seem to all fire all three events.
const checkbox = document.querySelector('input[type=checkbox]');
for (const event of ['input', 'click', 'change']) {
checkbox.addEventListener(event, () => {
log.textContent = `${event}\n${log.textContent}`
})
}
<label>Toggle <input type="checkbox" name="" id="">
</label>
<pre id="log"></pre>
As per the docs change and input seem equivalent; click does not seem equivalent to the other 3 as per the docs but in practice it seems equivalent.
Do we really have 3 events that duplicate each other's functionality? Does it matter in any way which event I use?
Or am I missing something?

These 3 events duplicate each other's functionality because you are looking at a checkbox which happens to be a special case.
For example, if you were to take a text field
The event input will fire whenever the text in an element is changed using the user interface.
The event change will fire (on most browsers) whenever the text element loses focus. It would only be triggered once instead of after every keystroke.
The event click will fire whenever a user clicks on the text field.
If we were to apply this to checkboxes (keeping in mind there is only one thing a checkbox can be changes into: either checked => unchecked orunchecked => checked)
The event input will fire whenever the checked state is changed using user interface.
The event change will fire whenever the checked state has changed
in an element (or when the checkbox loses focus in IE).
The event click will fire after the check state has finished changing .
The 3 events have very similar functionality (almost duplicates) because they are all trying to do something different that functionally does the same thing on checkboxes. The only differences being subtle implementation details.
I would use click to avoid having issues from the user of diffrent browsers.

They are not duplicated. There are subtle differences.
change happens once the value or state changes, and the element loses focus.
$('input').on('change', function(){
console.log('changed');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="checkbox" value="1">
<input type="text">
click happens once the element is clicked.
input happens IMMEDIATELY once the value or state changes, before it loses focus. This happens regardless of if the state changes as per a mouse or keyboard event. A checkbox can change state by clicking it, or focusing on it and hitting the spacebar. A click event would not catch the spacebar state change.
$('input').on('change', function(){
console.log('changed');
});
$('input').on('input', function(){
console.log('input');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="checkbox" value="1">
<input type="text">
To test the lack of focus change and the spacebar change on the checkbox, you can click the input box and then shift+tab to focus the checkbox to hit spacebar. It appears from the fiddle that for checkboxes, the change and input events both happen any time it changes, even without the focus being lost.
This differs from how the text field behaves. So there appears to be some behavioral differences between the two elements in when the events are generated. The checkboxes appear to follow a less strict implementation of the pattern, as opposed to input boxes.

Related

How to automatically disable a text field after checkbox is ticked

I am currently working on a project, that requires me to implement the following:
I've got 2 input fields - a regular text field, and a checkbox.
What I want to do is - if the user fills in the text field, the checkbox should be automatically disabled. If the checkbox is checked instead, the text field should be disabled.
What I've come up with so far is:
<input type="text" name="num-input1" id="dis_rm" value=""
onblur="document.getElementById('dis_per').disabled = (''!=this.value);" >
<input type="checkbox" name="num-input2" id="dis_per" value=""
onblur="document.getElementById('dis_rm').disabled = (''!=this.value);" >
If I fill in the text field, the checkbox is successfully disabled. However, if I tick the checkbox, the text field remains available.
What am I missing?
If I were you I would:
Move my JS code out of the HTML and into a separate file or at least into a script element.
Use document.getElementById to find an item in the DOM. See https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById
Once you have the element from the DOM, add an event listener for the blur events like this myElement.addEventListener('blur', myCallbackMethod). See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener for more info.
Inside your callback method you can use event.target.checked to see if the element you've added the event listener to is checked.
Here is a little snippet to get you going:
const
textInput = document.getElementById('element-ID-here'),
checkbox = document.getElementById('element-ID-here');
function onTextInputBlurHandler(event) {
// if textinput value is empty then
// set disabled for checkbox to false
// else
// set disabled for chexbox to true
}
textInput.addEventListenet('blur', onTextInputBlurHandler);
<input type="text" name="num-input1" id="dis_rm" value=""/>
<input type="checkbox" name="num-input2" id="dis_per" value=""/>
With this info you should be able to get (a little) further. When you do, update your question with your JavaScript code and I am sure people will be happy to help you further.
People are bringing up great suggestions in the comments and answers for better code design and quality, but from a purely functional point of view, there are two core things that you should do to get the functionality that you are describing:
1) As mentioned by Paul S. use the checked property for your checkbox logic. Right now, you are checking to see if the checkbox value is not an empty string, but it will always be an empty string, because that's the value that you've assigned to the element:
<input type="checkbox" name="num-input2" id="dis_per" value="" <----- *here*
Nothing else in your code is changing that, so it will always fail the logic check.
However, the checked property automatically switches between true and false as you check and uncheck the input. To do the logic check that you are looking for using that, do this for your JavaScript!
document.getElementById('dis_rm').disabled = this.checked;
2) Switch the event that you are binding for (at least) the checkbox to the "change" event instead of "blur". For checkboxes, the "change" event will trigger when you click on the checkbox (or hit space bar), but the element still maintains its focus. The blur event Will only fire once the user moves the focus to another element of the page.
I'd also recommend using "change" for the text field (there's no point in running the check, if the value is the same when you leave the field as it was when you entered it), but it's not as important since, from a timing point of view, when the "change" event fires, it happens immediately after the "blur" event, so, from the user's point-of-view, the behavior would be the same.
When it's all said and done, if you made no other changes to your code to improve the code design/quality (Thijs made some great suggestions, BTW), this is the minimum change that you would need to get the functionality that you want:
<input type="text" name="num-input1" id="dis_rm" value=""
onblur="document.getElementById('dis_per').disabled = (''!=this.value);" >
<input type="checkbox" name="num-input2" id="dis_per" value=""
onchange="document.getElementById('dis_rm').disabled = (this.checked);" >

Aurelia one-way binding to a checkbox

I'm looking for a way to one-way bind a checkbox in aurelia, whilst still allowing the checkbox to accept a click from the user.
Assume a view similar to the following that displays one of a list of selectable items:
<template>
<div click.trigger="selected()">
<label......>${vm.code}</label>
<label....>${vm.description}</label>
<img...../>
<input type="checkbox" checked.one-way="vm.selected"></input>
</div>
</template>
The user should be able to click anywhere in the view to select the item, thus the click.trigger="selected()" is attached to the container. Within selected(), the vm.selected property that the checkbox is bound to is updated.
The checkbox should also be clickable as well, but should allow selection to be controlled by the selected() method.
readonly can not be used on the input control for the checkbox as that is used to control input.value and in this case it is the checked property that is of interest.
Calling preventDefault on the event args disables default checkbox checked behavior, which can be achieved in Aurelia by returning false from the click delegate. That would require attaching another handler to the input control, and has the problem that the click gets handled by the input control and doesn't bubble up to the delegate attached to the container (selected()) to actually control selection.
Maybe there is another approach to this that I am missing, one that I was considering was to use two font-awesome icons that look like checked and unchecked checkboxes and switch between the two based on the value of vm.selected.
Update
On the delegation of events this answer does solve the issue if you are not using a checkbox but the browser prevents the state from toggling on the checkbox so as we discussed the best approach is probably to use an icon or sprite to show the 'state' of the checked piece since the control should be one-way only, which can be accomplished like this -
<i class="fa fa-${this.selected ? 'check-square-o' : 'square-o'}"></i>
Original answer on delegation
If we use delegate instead of trigger then our event bubbles and we can handle it a bit more gracefully and prevent the state from changing -
<div click.delegate="clicked()">
<input type="checkbox" click.delegate="clicked(false)" checked.one-way="vm.selected" />
</div>
And then in our view model we can handle the click event that bubbles -
clicked(event){
if (event !== false) {
// do something
} else {
return false;
}
}
This isn't a perfect solution but it should prevent the event from occurring which changes the state of the control but still allow the event to take place at the div level.

Pass additional information into event handler

I have an event handler that is executed when an option in a selectbox is clicked.
$('#my-select').live('change', function(evt) {
....
});
Is there a way to pass the status of the Ctrl-key (pressed/not pressed) into the event handler ? evt does not contain this information because all key related attributes are undefined.
change event and the keys used to enter the value
The change event is not something that has keys associated. Please read jQuery's .change() documentation:
The change event is sent to an element when its value changes. This event is limited to elements, boxes and elements. For select boxes, checkboxes, and radio buttons, the event is fired immediately when the user makes a selection with the mouse, but for the other element types the event is deferred until the element loses focus.
The example is this:
You have some input field.
You enter "some name" text into the field.
Before going to some other field, you decide to change it to "some test", eg. by hitting backspace couple times, then you navigate to next field (eg. by using Tab key, or by tapping Next on iOS keyboard etc.),
The onchange event handler is fired, when you navigate to other field (or rather, as soon as the field loses focus), so the information about all keys used to enter value are pretty unrelated.
Solution using keypress event
To solve that problem, you would need to implement your solution using keypress event, eg. thanks to jQuery's .keypress() function. In such case, event object's ctrlKey attribute (listed eg. here) lets you know about the status of the Ctrl key. Example usage is here: https://stackoverflow.com/a/4604093/548696
Demo for saving keys on keypress/keydown/keyup and reading on change
It is available here:
http://jsfiddle.net/tadeck/vPu94/
The demo clears the recorded keys on focus, saves them on every keypress event (you can easily edit that and test different cases) and reads them (and outputs on the screen) whenever change event is fired (so when the changed field leaves focus).

onchange isn't working

I am learning JavaScript through the Head First series book by O'Reilly media, and I just reached a chapter where I have to use the onchange event.
I'm testing using Safari/OS X Lion, Firefox, Chrome, Opera, and IE/Windows, but got the same result.
Given this code:
<html>
<head>
<title>onChange Test</title>
<script type="text/javascript">
function itWorks(){
alert("it works!");
}
</script>
</head>
<body>
<form>
<input type="text" onchange="itWorks();" />
<input type="text" onchange="itWorks();" />
</form>
</body>
<html>
Is it correct to say that the onchange event works whenever we change from one field to another, whether it is activated only by clicking or by using the TAB key?
The onchange event fires when:
Focus leaves the field
if the value has changed since focus was gained
It doesn't matter how focus was lost, and focus doesn't need to move to another field (a link could be focused, or nothing in the document could be, etc).
"we change from one field to another, whether its by clicking or by
using the TAB key" -
Thats onblur.
the event you have coded fires whenever you change the value of the input, then leave the field. EG: Enter something into the field, then press the TAB key.
Your example code works as expected for me.
The behaviors you described is onfocus. onchange executes when the value of the input changes.
If you type something into the field, it should run.
"Just to clarify, the onchange event works whenever we change from one field to another, whether its by clicking or by using the TAB key, right?"
Yes - as long as the value has changed
I'm not sure what the question is tbh - your code works!
I tested it on jsfiddle.net - which is great for learing / testing javascript.
(you should close your html tag btw)...
This is what the HTML5 draft spec says:
The unfocusing steps are as follows:
If the element is an input element, and the change event applies
to the element, and the element does not have a defined activation
behavior, and the user has changed the element's value or its list of
selected files while the control was focused without committing that
change, then fire a simple event that bubbles named change at the
element.
Unfocus the element.
Fire a simple event named blur at the element.
Note that change can fire at other times too. The spec also says:
... any time the user commits a change to the element's value or list of
selected files, the user agent must queue a task to fire a simple
event that bubbles named change at the input element.
And goes on to provide a couple of examples of "committing a change"
An example of a user interface with a commit action would be a File
Upload control that consists of a single button that brings up a file
selection dialog: when the dialog is closed, if that the file
selection changed as a result, then the user has committed a new file
selection.
Another example of a user interface with a commit action would be a
Date control that allows both text-based user input and user selection
from a drop-down calendar: while text input might not have an explicit
commit step, selecting a date from the drop down calendar and then
dismissing the drop down would be a commit action.

javascript text input change event not firing

I have a text type input field and a checkbox.
If I change the text and then click outside the input box (or press enter or tab) the change event is thrown. But if I enter some text and then click directly on the checkbox using the mouse, only the checkbox change event seems to be thrown.
I have the following code:
<input type="text" name="text" class="update">
<input type="checkbox" name="check" class="update">
and this jQuery:
$('.update').change(function(){
console.log($(this));
});
Is this a known problem, and how can I make sure all change events are fired/thrown/caught in this setup?
To fire user changes, use the input event:
$('input').on('input',function(){...})
To fire code changes, use the DOMSubtreeModified event:
$('input').bind('DOMSubtreeModified',function(){...})
If you want to fire both user and code changes:
$('input').bind('input DOMSubtreeModified',function(){...})
The DOMSubtreeModified event is marked as deprecated and sometimes quite CPU time consuming, but it may be also very efficient when used carefully...
I'm not sure if I get it. But for me when I try to type in textfield and then click checkbox by mouse both events are fired. But you have to keep in mind that event 'change' for text input means that this input has to loose focus, as long as field is focused no change event ever will be triggered. This somehow might be your case. Checkboxes/radioboxes work different way tho. No need to loose focus.
Cheers.
P.S.
My test case:
http://dl.dropbox.com/u/196245/index16.html
The change event fires for both because you're listening to the update class.
The change event will not fire unless the input focus switched to other controls

Categories

Resources