How to keep cursor position when updating js knockout observed in expander

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);

        // 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();
            }
        }
    }
};
+4
source share
2

, , Vanilla Javascript. UpperCase, , , , . .

var stayUpperCase = function () {
  if(this.value != this.value.toUpperCase()){
    var caretPos = getCaretPosition(this);
    this.value = this.value.toUpperCase();
    setCaretPosition(this, caretPos);
  }
};
document.getElementById('myinput').addEventListener('keyup', stayUpperCase);
document.getElementById('myinput').addEventListener('change', stayUpperCase);

// Code from http://stackoverflow.com/questions/2897155/get-cursor-position-in-characters-within-a-text-input-field
function getCaretPosition (elem) {
  // Initialize
  var caretPos = 0;

  // IE Support
  if (document.selection) {
    // Set focus on the element
    elem.focus ();
    // To get cursor position, get empty selection range
    var sel = document.selection.createRange ();
    // Move selection start to 0 position
    sel.moveStart ('character', -elem.value.length);
    // The caret position is selection length
    caretPos = sel.text.length;
  }

  // Firefox support
  else if (elem.selectionStart || elem.selectionStart == '0'){
    caretPos = elem.selectionStart;
  }

  // Return results
  return (caretPos);
}

// Code from http://stackoverflow.com/questions/512528/set-cursor-position-in-html-textbox
function setCaretPosition(elem, caretPos) {
  if(elem != null) {
    if(elem.createTextRange) {
      var range = elem.createTextRange();
      range.move('character', caretPos);
      range.select();
    }
    else {
      if(elem.selectionStart) {
        elem.focus();
        elem.setSelectionRange(caretPos, caretPos);
      }
      else
        elem.focus();
    }
  }
}
<input type="text" id="myinput"/>
Hide result

, .

+1

jQuery, Thanasis Grammatopoulos, CSS :

.upper-case { text-transform: uppercase; }

, :

<input type="text" maxlength="16" class="upper-case" data-bind="value: fhaNum, valueUpdate: 'afterkeydown', disable: isReadOnly" />

, :

var data = {
    "fhaNum": self.fhaNum().toUpperCase()
};
+3

All Articles