我有一个自定义组件,允许用户输入文本并将其发送到后端,在那里我进行一些计算并使用html将新文本吐出来。
我的问题是当用户键入此textarea时,它会反转所有文本并将游标保留在textarea的开头。所以现在' foo bar'因为我加入了watch
,所以只发生了这种情况。我可以删除观察者,但是当我将foo
设置为等于来自父级的内容时,我需要它(或者需要另一种方式)将我的更新应用到textarea,通过foo
变量。
console.log(v)
写出反向文本。
知道怎么改变这个吗?
自定义组件:
<template>
<div contenteditable="true" @input="updateHTML" class="textareaRoot"></div>
</template>
<script>
export default {
name: 'htmlTextArea',
props:['value'],
mounted: function () {
this.$el.innerHTML = this.value;
},
watch: {
value(v) {
this.$el.innerHTML = v; //v is the reverse text.
}
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML);
}
}
}
</script>
使用自定义组件的父级:
<htmlTextArea id="textarea" v-model="foo"></htmlTextArea>
...
<script>
...
methods: {
triggerOnClick() {
this.foo = 'something';//Without the watcher, when I change this.foo to something the actual textarea does not display the new data that I assigned to foo. But in Vue dev tools I can see the new change.
}
更新:
Vue.component('html-textarea',{
template:'<div contenteditable="true" @input="updateHTML"></div>',
props:['value'],
mounted: function () {
this.$el.innerHTML = this.value;
},
watch: {
value(v) {
this.$el.innerHTML = v;
}
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML);
}
}
});
new Vue({
el: '#app',
data () {
return {
foo: '',
}
}
});
&#13;
<script src="https://unpkg.com/vue"></script>
<div id="app">Type here:
<html-textarea spellcheck="false" id="textarea" v-model="foo"> </html-textarea>
</div>
&#13;
答案 0 :(得分:1)
问题在于,当您设置innerHTML
元素的contenteditable
时,您将失去选择(光标位置)。
因此,在设置时应执行以下步骤:
innerHTML
; 保存和恢复是棘手的部分。幸运的是,我得到了这两个方便的功能,可以完成最新IE和更新版本的工作。见下文。
function saveSelection(containerEl) {
var range = window.getSelection().getRangeAt(0);
var preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(containerEl);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
var start = preSelectionRange.toString().length;
return {
start: start,
end: start + range.toString().length
}
}
function restoreSelection(containerEl, savedSel) {
var charIndex = 0, range = document.createRange();
range.setStart(containerEl, 0);
range.collapse(true);
var nodeStack = [containerEl],
node, foundStart = false,
stop = false;
while (!stop && (node = nodeStack.pop())) {
if (node.nodeType == 3) {
var nextCharIndex = charIndex + node.length;
if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
range.setStart(node, savedSel.start - charIndex);
foundStart = true;
}
if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
range.setEnd(node, savedSel.end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
var i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
Vue.component('htmltextarea', {
template: '#hta',
name: 'htmlTextArea',
props:['value'],
mounted: function () {
this.$el.innerHTML = this.value;
},
watch: {
value(v) {
if (v === 'yes') {
let selection = saveSelection(this.$el);
this.$el.innerHTML = 'no!';
this.$emit('input', 'no!');
restoreSelection(this.$el, selection);
}
}
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML);
}
}
});
new Vue({
el: '#app',
data: {
foo: 'Clear this and type "yes" (without the quotes). It should become "no!".'
}
})
&#13;
<script src="https://unpkg.com/vue"></script>
<div id="app">
<htmltextarea id="textarea" v-model="foo"></htmltextarea>
<hr>
Result: <pre>{{ foo }}</pre>
</div>
<template id="hta">
<div contenteditable="true" @input="updateHTML" class="textareaRoot"></div>
</template>
&#13;
在您的应用中,我建议您将它们放在专用的.js
文件中,以便更好地进行组织。