The goal is for user input to be uppercase as you type. I am using the following expander:
ko.extenders.uppercase = function (target, option) {
target.subscribe(function (newValue) {
target(newValue.toUpperCase());
});
return target;
};
This extender can be added to the observable associated with HTML text fields:
self.userField = ko.observable(product.userField || "").extend({ uppercase: true });
This works great. Converts text bound to observable to uppercase. Because the user enters text in an empty field or adds it to the end of existing text, the text is converted to uppercase.
So far so good!
Now, when the user wants to insert text, the cursor is placed at the insertion point either with the cursor keys or the mouse, and new text is entered, the character is entered as upper case, but the cursor position now jumps to the end of the text, because the base observable is updated.
How to keep cursor position while continuing to convert user input to uppercase?
Update 1:
I created a custom binding to access the element and get and set the cursor position. I tried using an existing binding value. I use the built-in function ko.selectExtensions.writeValue. This may be good enough for my application, but may not work in all browsers.
ko.bindingHandlers.upperCase = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
ko.bindingHandlers["value"].init(element, valueAccessor, allBindings);
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var caretPosition = getCaretPosition(element);
var newValue = ko.utils.unwrapObservable(valueAccessor());
ko.selectExtensions.writeValue(element, newValue.toUpperCase());
setCaretPosition(element, caretPosition);
function getCaretPosition (ctrl) {
var CaretPos = 0;
if (document.selection) {
ctrl.focus ();
var Sel = document.selection.createRange ();
Sel.moveStart ('character', -ctrl.value.length);
CaretPos = Sel.text.length;
}
else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
CaretPos = ctrl.selectionStart;
}
return (CaretPos);
}
function setCaretPosition(ctrl, pos) {
if(ctrl.setSelectionRange) {
ctrl.focus();
ctrl.setSelectionRange(pos,pos);
}
else if (ctrl.createTextRange) {
var range = ctrl.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
}
}
};