如何在扩展器中更新Knockout js Observable时保留光标位置

时间:2014-10-08 17:45:50

标签: javascript knockout.js

目标是在用户输入键入时将其转换为大写。我使用以下扩展程序:

ko.extenders.uppercase = function (target, option) {
    target.subscribe(function (newValue) {
        target(newValue.toUpperCase());
    });
    return target;
};

可以将此扩展程序添加到绑定到HTML文本框的observable:

self.userField = ko.observable(product.userField || "").extend({ uppercase: true });

这很有效。将绑定到observable的文本转换为大写。当用户将文本键入空字段或将其添加到现有文本的末尾时,文本将转换为大写。

到目前为止,非常好!

现在,当用户想要插入文本时,光标将被放置到插入点,或者使用光标键或鼠标,并且输入新文本,字符将作为大写插入,但光标位置现在跳转到结束文本,因为底层的observable已更新。

如何在将用户输入转换为大写的同时保留光标位置?

更新1:

我创建了一个自定义绑定,因此我可以访问该元素并获取并设置光标位置。我试图利用现有的value绑定优点。我正在使用内置的ko.selectExtensions.writeValue函数。这可能对我的应用程序来说足够好,但可能无法在所有浏览器中使用。

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

2 个答案:

答案 0 :(得分:3)

在测试了自定义绑定和Thanasis Grammatopoulos发布的基于jQuery的解决方案之后,我决定采用CSS路线并首先创建了一个新类:

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

然后我将它应用于我想要显示为大写的输入元素:

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

由于这不会将文本保存为observable中的大写,因此我在将值发送到服务器之前将其设置为高位:

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

答案 1 :(得分:1)

嗯,我认为最好的解决方案是使用Vanilla Javascript。如果没有,检查值是否为UpperCase,获取位置,更改值,设置位置。容易。

&#13;
&#13;
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();
    }
  }
}
&#13;
<input type="text" id="myinput"/>
&#13;
&#13;
&#13;

您可以附加更多键盘事件,使其看起来更流畅。