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,也许这比调用它“懒惰”更好。由你决定。

请注意,“输入”事件并不需要每次都解除绑定。当元素不可编辑时,它无论如何都不会触发。

于 2013-10-15T09:46:43.620 回答
3

@Tomalak 的答案是完美的,因此我赞成。

但对于那些到达这里的人,比如我自己:

  • 寻找一次性的自定义绑定
  • 假设内容始终是可编辑的
  • 可以使用value:-esque 更新(即:模糊)

我建议如下:

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

        function onBlur(){
            if (ko.isWriteableObservable(value)) {
                value(this.innerHTML);
            }
        };                

        element.innerHTML = value(); //set initial value
        element.contentEditable = true; //mark contentEditable true
        element.addEventListener('blur', onBlur); //add blur listener
    }
};
于 2017-04-08T22:54:11.870 回答
1

在以前,我遇到了通过此代码解决它的 bdo 问题。此代码也可用于其他 contentEditable。所以我建议如下:

 ko.bindingHandlers.bdoValue =
 ko.bindingHandlers.contentEditable = {
      'init': function(element, valueAccessor) {
          var updateHandler = function() {
              var modelValue = valueAccessor(),
                  elementValue = element.innerHTML;
              modelValue(elementValue);
          };
          ko.utils.registerEventHandler(element, "keyup", updateHandler);
          ko.utils.registerEventHandler(element, "input", updateHandler);
      },
      'update': function(elem, valueAccessor) {
          var value = ko.utils.unwrap(valueAccessor())||"";
          var current = elem.innerHTML;
          if (value !== current) {
              elem.innerHTML = value;    
          }
      }
   };
于 2019-09-30T08:01:00.310 回答