Reputation: 531
I have an input field that holds a currency value. I am trying to display formatted currency amount when the input field does not have focus. The input field type is set to number. While this works fine on Chrome, IE and Edge; on firefox, the input textbox loses focus immediately after getting it. Here is the simplified jsfiddle
https://jsfiddle.net/nkkfmLmc/2/
This is similar to example 2 at http://knockoutjs.com/documentation/hasfocus-binding.html, except for the number field. The problem is resolved if I change the input type to text from number (but also lose all the goodness). Any pointers or workarounds would be appreciated.
HTML:
<p>
Name:
<b data-bind="visible: !editing(), text: '$'+amount(), click: edit"> </b>
<input type="number" min="0" max="100" step="1" data-bind="visible: editing, value: amount, hasFocus: editing" />
</p>
<p><em>Click the amount to edit it; click elsewhere to apply changes.</em></p>
JS:
function PersonViewModel(amount) {
// Data
this.amount = ko.observable(amount);
this.editing = ko.observable(false);
// Behaviors
this.edit = function() { this.editing(true) }
}
ko.applyBindings(new PersonViewModel(51.22));
Upvotes: 1
Views: 1534
Reputation: 531
Got it working by using an extra observable to control visibility and adding a throttle to the observable that controls focus. Looks like the hasFocus binding check was happening before the element had become visible.
https://jsfiddle.net/nkkfmLmc/8/
JS:
<p>
Name:
<b data-bind="visible: !editingWrapper(), text: '$'+amount(), click: edit"> </b>
<input type="number" min="0" max="100" step="1" data-bind="visible: editingWrapper, value: amount, hasFocus: editing" />
</p>
<p><em>Click the amount to edit it; click elsewhere to apply changes.</em></p>
HTML:
function PersonViewModel(amount) {
// Data
var self = this;
this.amount = ko.observable(amount);
this.editing = ko.observable(false).extend({throttle:200});
this.editingWrapper = ko.observable(false);
// Behaviors
this.edit = function() { self.editingWrapper(true);self.editing(true); }
this.editing.subscribe(function(newVal){
self.editingWrapper(newVal);
})
}
ko.applyBindings(new PersonViewModel(51.22));
Upvotes: 0
Reputation: 23372
The hasFocus
binding works with Document.activeElement
to determine which element is in focus:
Source: https://github.com/knockout/knockout/blob/master/src/binding/defaultBindings/hasfocus.js#L17
var ownerDoc = element.ownerDocument;
if ("activeElement" in ownerDoc) {
var active;
try {
active = ownerDoc.activeElement;
} catch (e) {
// IE9 throws if you access activeElement during page load (see issue #703)
active = ownerDoc.body;
}
isFocused = (active === element);
}
When stepping through this binding in your fiddle, I noticed it returns the body
element as the document's activeElement
, making isFocused
to be false
.
On the MDN documentation page about activeElement
, I then read:
Note: On Mac, elements that aren't text input elements tend not to get focus assigned to them.
Which, to me, suggests there's simply no support for this binding in a number
input.
I'd suggest to either:
document.activeElement
.The knockout developers probably had a good reason to stick with the activeElement
approach. In their comments they state:
// Where possible, ignore which event was raised and determine focus state using activeElement,
// as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
// However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
// prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
// from calling 'blur()' on the element when it loses focus.
// Discussion at https://github.com/SteveSanderson/knockout/pull/352
So it might be wise to go for the first option...
Upvotes: 2