TinyMCE ほど高機能な WYSIWYG エディタでなくても、カーソル位置に文字列を挿入する機能くらいつけたいなぁなんてことありませんか
そこで今回ご紹介するのが「caret binding」です
caret binding
caret-binding
ko.bindingHandlers.caret = {
init: function(element, valueAccessor, allBindings) {
var $elm = $(element),
suspend = true,
value_writer = allBindings()._ko_property_writers.value;
function updateModel() {
var pos = $elm.caret();
var modelValue = valueAccessor();
suspend = true;
if (ko.isObservable(modelValue)) modelValue(pos);
else valueAccessor(pos);
}
$elm.bind('keydown click focus', updateModel);
ko.computed(function() {
var modelValue = ko.unwrap(valueAccessor());
if (suspend) {
suspend = false;
if (typeof modelValue == 'number') return;
}
var st = element.scrollTop;
$elm.caret(modelValue);
element.scrollTop = st;
if (typeof modelValue == 'string') {
updateModel();
if (value_writer) {
value_writer($elm.val());
}
}
});
}
};
依存: jQuery, jquery.caret
使い方
<input type="text">
や <textarea>
にバインドして使います。
<textarea data-bind="value: body, caret: bodyCaret"></textarea>
function ViewModel() {
var self = this;
self.body = ko.observable("");
self.bodyCaret = ko.observable();
self.insertFoo = function() {
self.bodyCaret('Foo');
};
}
バインドした bodyCaret
には常に文字のキャレット位置が格納されますが、ViewModel 側から文字列を書きこむことでキャレット位置に挿入することができます。
またそのままだとなぜかスクロール位置が末尾に飛んでしまうため、その補正もかけています。
謝罪
元々は別のタイトルを考えていましたが、どうにも考えがまとまらずに諦めてかなりミニマムな記事になってしましました。
申し訳ございません。
ですが、個人的にはこういった「こんなカスタムバインディング書いたよ!」というミニマムな記事が増えてほしいと思っています。リユーザブルな知見を共有しましょう!