Aurelia one-way binding to a checkbox - javascript

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.

Related

Click vs Input vs Change for checkboxes?

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.

jQuery input change doesn't work

I'm trying to make something like a shopping cart, but just with an order form.
I am using this pattern to fire input changes, but it doesn't work in my case.
Here what I have first.
<div class="ingrid__table-row">
<div class="ingrid__table-data ingrid__table-item">Lemon</div>
<div class="ingrid__table-data ingrid__table-weight">15g</div>
<div class="ingrid__table-data ingrid__table-price">10</div>
</div>
With jQuery, on click, I take the data from ingrid__table-data and add to the suitable input into .order__container.
Then, on the same click, a number input is appended, which will enable to choose the quantity of the selected products.
$('.order__container').append(`<input class="bul-order-info__input bul-order-info__qnt" type="number" name="Quantity" min="1" value="1">`)
And it appears on a webpage in the order form.
I need to detect the value changes of "number type input" and fire other events.
But the input changes are not detected, although if I create the same input element manually in HTML document, these changes are detected perfectly as it's shown here
How can I achieve this behavior?
My best guess based on the info you provided is that you are trying to attach the on change event to the dynamically created inputs on this way:
$('.bul-order-info__input').change( function () {...} );
But with the code before you are aren't applying those changes to any input because none of them exists when you are creating the event handlers, so you have to bind the events to an existing element like this:
$(document).on('change', '.bul-order-info__input', function() {...});
The element doesn't have to be always document, but I tend to use it, because is the only one that always will be present. However, something like this is also valid:
$('.order__container').on('change', '.bul-order-info__input', function() {...});

How to detect click event on dropdown, but not on the options menu in Javascript (or differentiate between the two)?

So I have a simple dropdown in my code that I attach a click hander to, like so (code is technically typescript)
this.highElement.click(() => {
console.log(event);
});
This event is triggered both when I click the select box AND when I click one of the options (Here's a Js fiddle that demonstrates what I mean https://jsfiddle.net/Kolichikov/zmdL6q2d/). What I would like ideally is for the event to only fire when I click the select box itself, not the resulting list of items.
I tried setting a delegated event for the option this.highElement.on("click", "option", () => { console.log("option!"); });, but that didn't work.
I have noticed that the MouseEvent properties are different (the mouse coordinates aren't populated when an item is clicked), but this seems like a browser implementation that could change (maybe?).
Is there a way to properly differentiate between the two events?
I'll give you 2 options, you pick the one it's best for you:
First you can differentiate between the 2 events like this:
$("#selectItem").click(function(event){
if (event.target.id == 'selectItem')
console.log(event)
});
the event has a different target when it returns from clicking in select and from choosing an option.
But I think it would be much clearer if you used the change event
$("#selectItem").change(function(event){
console.log(event)
});
it fires only when you select an item. Depending on what you need this could be better.
EDIT
Checkingwhat #libzz said, I notice that the first part of the above response is wrong. I didn't edit it because the OP accepted the answer as is and I'd be changing the code that lead to his decision.
But as #libzz said, the event when fired has always the same id.
What I also noticed now is that the click event only fires when an option is clicked, not when the select box is clicked. That levels the onclick with the onchange event. They basically do the same thing in this case.
So in order to make code clearer, the best would be using only onchange event for selects.

After execution of onclick-callback the 'checked' state is reset

This drives me mad. I just can't understand it.
I wrote a filter-function based on checkboxes and clicking on their labels. I'm checking the 'checked' state of checkboxes and show mathed elements of the list (the rest elements are hidden). I use 3rdparty plugin that stylizes checkboxes (cut from example) and makes checkboxes checked while other onClick event does the filtering.
The problem is that after 'checked' state is successfully set inside a callback-function it "suddenly" becomes reset! I can't understand why that happens.
I implemented the base logic (without stylizing) here: http://jsfiddle.net/3Xtuh/13/
and ask all to help me solve this, please.
The problem is that you are invoking the click event manually, and then when your function is done running, the default click event is invoked.
By passing the event variable to your click handler and calling event.preventDefault(); fixes this behavior.
See example here: http://jsfiddle.net/3Xtuh/14/
The HTML label will check the associated checkbox even if it´s hidden (using CSS) so there´s no need to reinvent the wheel.
You should use the change() event. Try this demo and view your console.
It's default browser behavior that's messing your js script. By default, clicking on a label that is either wrapped around checkbox or have valid for attribute set, is toggling checked state of that checkbox.
You've attached custom onclick handler on labels.
So what's going on is that when clicking on a label? Your click handler gets fired (in in you alter state of target checkbox), and then

Check Value of a FORM POST on submit using jquery

I have 3 buttons <input type="submit" name="submitButton" value="something">. Then i check the event with $(".form_name").submit(function(e) { and i would like to check which of the 3 buttons have been clicked and to alert the user of the action and return false if the user wishes to not go through with the action.
How do i check which of these buttons have been clicked?
event.target might help. Another workaround is to have the three buttons fire events, and make their event handler do the submission. In that sense there is no guesswork.
—
Personally I would first make sure that the three buttons have identifiers, plausibly tucked within with $(element).data lines, or custom HTML attributes (oh, don’t use special classes!), and have the event handler check $(event.target).data(key) or $(event.target).attr(key) to find out more information related to the caller. I would then map the form’s submit event also to this handler, so everything is processed from one place.
The Event interface gives information about the event. Look at this answer to know where you can find this object.
The target or scrElement of the object is the attribute you're looking for. See Quirksmode.org.
EDIT: you asked for a jQuery specific answer. You can just use event.target (in your case e.target), jQuery fixes this when necessary.

Categories

Resources