16

要素が contentEditable かどうかを切り替えるカスタム バインディング ハンドラーを作成しました。また、要素のコンテンツが編集されたときに html バインディングを更新するようにしたいので、入力イベントをリッスンし、利用可能な場合は html バインディングを更新します。

ko.bindingHandlers.contentEditable = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.unwrap(valueAccessor());
        element.contentEditable = value;

        var $element = $(element);

        if (value) {
            var allBindings = allBindingsAccessor();
            var htmlBinding = allBindings.html;

            if (ko.isWriteableObservable(htmlBinding)) {
                $element.on("input", function (event) {
                    htmlBinding(element.innerHTML);
                });
            }
        } else {
            $element.off("input");
        }
    }
};

ただし、ここに問題があります。

  • ユーザーが要素に何かを入力します
  • 入力イベントが発火する
  • HTMLバインディングが更新されました
  • 要素の innerHTML が更新されます
  • 要素内のカーソル位置が先頭に戻ります

jsfiddle は千の言葉を言います... http://jsfiddle.net/93eEr/1/

これを正確に処理する方法については、少し困惑しています。

4

3 に答える 3

22

ko.bindingHandlers.htmlLazy = {
    update: function (element, valueAccessor) {
        var value = ko.unwrap(valueAccessor());
        
        if (!element.isContentEditable) {
            element.innerHTML = value;
        }
    }
};
ko.bindingHandlers.contentEditable = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.unwrap(valueAccessor()),
            htmlLazy = allBindingsAccessor().htmlLazy;
        
        $(element).on("input", function () {
            if (this.isContentEditable && ko.isWriteableObservable(htmlLazy)) {
                htmlLazy(this.innerHTML);
            }
        });
    },
    update: function (element, valueAccessor) {
        var value = ko.unwrap(valueAccessor());
        
        element.contentEditable = value;
        
        if (!element.isContentEditable) {
            $(element).trigger("input");
        }
    }
};

var viewModel = {
    editable: ko.observable(false),
    content: ko.observable("<i>This</i> is the initial content!")
};

ko.applyBindings(viewModel);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<label>Editable: <input type="checkbox" data-bind="checked: editable"/></label>
<hr>
<div data-bind="contentEditable: editable, htmlLazy: content"></div>
<hr>
<pre data-bind="text: content"></pre>

最小限の変更でトリックを行います。http://jsfiddle.net/93eEr/3/を参照

バインディング ハンドラを呼び出すこともできますが、htmlEditable「怠け者」と呼ぶよりはましかもしれません。君による。

「入力」イベントは、毎回アンバインドする必要はないことに注意してください。要素が contenteditable でない場合はとにかく起動しません。

于 2013-10-15T09:46:43.620 に答える