我把有问题的简化版代码放在下面,方便大家亲手实验效果。
只要有methods最后一行this.testdiv = temp,就出现非常奇葩的BUG:在灰框中按回车然后随便输入点什么,再点到灰框外面(失焦),v-for会多次渲染回车后输入的文字,console中的数据也变得混乱。重复失焦事件,会不停渲染这些文字。不按回车只编辑示例条目,再失焦没事。
把this.testdiv = temp注释掉,console中的数据就很正常,但这样就无法实现我想要的功能。
不能理解为什么,搞了一天都没解决,不知道哪里犯错了。求大神指教。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<title>contenteditable实验</title>
</head>
<body>
<style>
*{min-height: 20px;} #a{background: #e6e6e6;}</style>
<div id="app">
<div id="a" contenteditable="true" @blur="archiveTest()">
<div v-for="(content, index) in testdiv" contenteditable="true"
:data-index="index" :key="index" v-html="content"></div>
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
//试验用数据
testdiv: ['条目1','条目2','条目3'],
},
methods: {
//创建一空数组temp,并循环取得元素#a所有子节点的innerHTML
//后用temp替换data中的数据
archiveTest: function() {
let temp = [];
let achildren = document.getElementById('a').children;
for (let i=0; i<achildren.length; i++){
let text = achildren[i].innerHTML;
temp.push(text);
}
console.log(temp);
this.testdiv = temp;
},
},
});
</script>
</body>
</html>
顺便回复第一位回答的大神的方法(而且在实在没其它办法的情况下只能退而求其次这样么干),我想要的效果是在父元素F的子元素A的开始区域内按删除键能够直接删除前一个B子元素内的内容。如果父元素的contenteditable=false,那已存在的子元素就无法直接删除(做个按钮用鼠标点选不算),如果说这个还能通过某些迂回的方法(比如监听退格键并查看光标的位置总之繁琐)做到的话,要跨子元素选择文字(跨段)就无法做到。
并且我希望每一分段都能成单独的条目被存储在数组中。主要是想偷懒,选取DOM并抽取里面的innerTEXT或innerHTML就很简单,用其它方法比如用execCommand设置分隔符为<br />然后用正则分段抽取文字的方法就很烦……
我理解的v-for是基于数组来渲染,每当数组变化时,会根据现有的数组渲染新的DOM,于是我就在每次失焦后解析现有的DOM形成全新的数组……但试验下来发现v-for好像不是这么工作的……闹心……
试验下来的感觉就是没法用同一个数组双向绑定,必须用两个数组,其中一个是初始状态数组用于首次渲染,然后再搞一个终结状态数组存放编辑后的的文本。
另外,我怀疑这个渲染混乱的产生原因是和在contenteditable=true的情况下,每次按回车键生成的新div的index(可从console中页面元素的data-index属性观察到)都和前一个div相同有关。因为每次重复渲染的都是那几条。
PS:假如把@blur改成@input,按回车后输入,那个渲染出错简直有毒……
原因如下
在父级元素赋予contenteditable的时候, 每次回车都是相当于在内部创建一个新的子元素
你相当于修改了 子元素, 也修改了父元素, 所以两个元素都会作出反馈
也就是 子元素处理了 父级也处理了, 子集会生成新的div标签在内部, 所以你现在需要
去掉父级的 contenteditable="true" @blur="archiveTest()"
把 @blur="archiveTest()" 放在子集上, 然后递归解析子集便签