Reputation: 309
I've written a custom binding handler that toggles whether or not an element is contentEditable. I also want any html bindings to update when the element's contents are edited, so it listens for input events and updates the html bindings if available.
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");
}
}
};
However, here is the problem:
A jsfiddle says a thousand words... http://jsfiddle.net/93eEr/1/
I'm a bit stumped as to how exactly to handle this.
Upvotes: 16
Views: 6452
Reputation: 407
In previous I have problem with bdo that resolve it by this code. also this code is usable for other contentEditable. so I suggest the following:
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;
}
}
};
Upvotes: 1
Reputation: 12587
@Tomalak's answer is perfect, hence my upvote.
But for those arriving here, like myself:
value:
-esque updates (ie: on blur)I suggest the following:
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
}
};
Upvotes: 4
Reputation: 338326
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>
do the trick with minimal change. See http://jsfiddle.net/93eEr/3/
You could call the binding handler htmlEditable
, maybe that's better than calling it "lazy". Up to you.
Note that the "input" event does not really need to be unbound every time. It won't fire anyway when the element is not contenteditable.
Upvotes: 23