Нокаут contentEditable binding
Я написал специальный обработчик привязки, который переключает, является ли элемент 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
- Позиция курсора в элементе возвращается к началу
A jsfiddle говорит тысячу слов... http://jsfiddle.net/93eEr/1/
Я немного озадачен тем, как именно с этим справиться.
Ответы
Ответ 1
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>
Ответ 2
@Ответ на ТОМАЛАК совершенен, следовательно, мой верхний.
Но для тех, кто прибывает сюда, как и я:
- поиск одноразовых пользовательских привязок
- который предполагает, что содержимое всегда доступно для редактирования
- в порядке с
value:
-специальными обновлениями (то есть: по размытию)
Я предлагаю следующее:
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
}
};
Ответ 3
В предыдущих у меня есть проблемы с 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;
}
}
};