The goal is to have user input converted to upper case as they type. I am using the following extender:
ko.extenders.uppercase = function (target, option) {
target.subscribe(function (newValue) {
target(newValue.toUpperCase());
});
return target;
};
This extender can be added to the observable that are bound to HTML text boxes:
self.userField = ko.observable(product.userField || "").extend({ uppercase: true });
This works great. Converts text bound to the observable to upper case. As the user types text into an empty field or adds it to the end of existing text, the text is converted to upper case.
So far, so good!
Now when the user wants to insert text, the cursor is placed to the insertion point either with the cursor keys or mouse and the new text is entered the character is inserted as upper case but the cursor position now jumps to the end of the text because the underlying observable is updated.
How can the cursor position be retained while still converting the user input to upper case?
Update 1:
I created a custom binding so I could access the element and get and set the cursor position. I tried to tap into the existing value
binding goodness. I am using the built in ko.selectExtensions.writeValue
function. 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);
// Code from: http://stackoverflow.com/questions/2897155/get-cursor-position-in-characters-within-a-text-input-field
function getCaretPosition (ctrl) {
var CaretPos = 0;
// IE Support
if (document.selection) {
ctrl.focus ();
var Sel = document.selection.createRange ();
Sel.moveStart ('character', -ctrl.value.length);
CaretPos = Sel.text.length;
}
// Firefox support
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();
}
}
}
};