I attempted to use John Earles custom binding above (Thanks John!) together with a text field that also used a valueUpdate: 'afterkeydown' binding and found out that that didn't really work as expected. (I'm guessing that this is because all bindings fire again when one of the bindings need to fire, and valueUpdate most likely causes the value binding to fire after each character has been written).
After a couple of attempts I did a semi-fix for this issue that seems to be working alright for me. The basic idea is that before we fire the hasfocus binding we check if the element in question already has focus and we only actually select the text when the element didn't actually have focus before the hasfocus binding has fired.
I have used jquery to check for focus, but you could probably do it in some other way as well.
ko.bindingHandlers.hasSelectedFocus = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
ko.bindingHandlers['hasfocus'].init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var focusBefore = $(element).is(':focus');
ko.bindingHandlers['hasfocus'].update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
var selected = ko.utils.unwrapObservable(valueAccessor());
if (selected && !focusBefore) {
element.select();
}
}
};
Edit: I've noticed that this kind of binding might not work exactly as you want when used on an iOS device. There's nothing wrong with the binding as such, but the autofocus and select logic causes the devices keyboard to come up as soon as the binding executes which may or may not be exactly what you want to happen on such a device. To compare, on the android devices that I use to test I do not automatically get the keyboard as soon as this binding executes. For my sake I ended up creating yet another binding to do nothing on iOS devices in the following way.
ko.bindingHandlers.hasNonIosSelectedFocus = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (navigator.userAgent.match(/iPad/i) == null && navigator.platform.indexOf("iPhone") == -1 && navigator.platform.indexOf("iPod") == -1) {
ko.bindingHandlers['hasSelectedFocus'].init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
}
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (navigator.userAgent.match(/iPad/i) == null && navigator.platform.indexOf("iPhone") == -1 && navigator.platform.indexOf("iPod") == -1) {
ko.bindingHandlers['hasSelectedFocus'].update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
}
}
};
Tl;dr:
If you use this and want to cater to tablets/smartphones then be sure to test that this is the interaction logic that you actually expect.