在 Vue 组件内使用 Rangy 恢复文本范围时出现错误消息 "Marker element has been removed"
Error message "Marker element has been removed" when restoring text ranges with Rangy inside Vue component
我有一个相当复杂的 Vue 组件,它涉及 contenteditable
div。我想使用 Rangy 突出显示此 div 中的单词并添加额外的标记,即使在编辑文本时也保留此标记。
最初,我打算 post 一个问题,因为在某些时候处理额外的标记使 contenteditable
div 无法编辑,我无法删除或添加字符。但是当我尝试设置代码片段时,我收到了另一条错误消息。
编辑 contenteditable
div:
时我预计会发生三件事
在 storeIndexes
方法中,我为 highlights
数组中的每个元素创建并存储范围。这个方法叫做@beforeinput
。此事件并非在所有浏览器中都可用,我使用的是 Chrome.
接下来,我希望 contenteditable
div 中的文本得到更新。
最后,范围应该通过称为 @input
的 restoreIndexes
方法恢复。
我知道我的代码不应该有任何可见的效果。我的问题是尝试编辑文本时出现错误消息:Rangy warning: Module SaveRestore: Marker element has been removed. Cannot restore selection.
这是怎么回事?
new Vue({
el: '#app',
data: {
currentHighlights: [],
highlights: [
{
start: 10,
end: 20
}
],
},
methods: {
// What happens just before an edit is applied
storeIndexes: function(event) {
// Create a new range object
let range = rangy.createRange();
// Get contenteditable element
let container = document.getElementById('text-with-highlights');
// Store all currently highlights and addd DOM markers
this.highlights.forEach(highlight => {
// Move range based on character indexes
range.selectCharacters(container, highlight.start, highlight.end);
// Set DOM markers and store range
this.currentHighlights.push(rangy.saveRange(range))
});
},
// What happens after an edit was made
restoreIndexes: function(event) {
// Create a new range object
let range = rangy.createRange();
// Get range based on character indexes
let container = document.getElementById('text-with-highlights');
this.currentHighlights.forEach(highlight => {
range.selectCharacters(container, highlight.start, highlight.end);
rangy.restoreRange(range);
});
this.currentHighlights = [];
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-core.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-selectionsaverestore.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-textrange.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id='app'>
<div @beforeinput='storeIndexes' @input='restoreIndexes' contenteditable id='text-with-highlights'>
Just some text to show the problem.
</div>
</div>
事实证明这不是 Vue 问题,而是异步代码 运行 之一:restoreIndexes
尝试恢复范围时 storeIndexes
未完成。
setTimeout
成功了。我不确定是否有比将方法延迟一些随机间隔更好的方法,
// What happens after an edit was made
restoreIndexes: function(event) {
setTimeout(() => {
// Create a new range object
let range = rangy.createRange();
// Get range based on character indexes
let container = document.getElementById('text-with-highlights');
this.currentHighlights.forEach(highlight => {
range.selectCharacters(container, highlight.start, highlight.end);
rangy.restoreRange(range);
});
}, 10);
// Restore highlights
this.currentHighlights = [];
},
但是,我可以使用 v-runtime-template 库完全摆脱我的 storeIndexes
方法。这是 v-html
的替代方法,但也适用于以编程方式插入的元素,例如我的问题中的突出显示。
现在,我的高亮显示仅对 $data
中索引的变化做出反应,我不需要在 contenteditable
div 更新时手动移动它们。
我有一个相当复杂的 Vue 组件,它涉及 contenteditable
div。我想使用 Rangy 突出显示此 div 中的单词并添加额外的标记,即使在编辑文本时也保留此标记。
最初,我打算 post 一个问题,因为在某些时候处理额外的标记使 contenteditable
div 无法编辑,我无法删除或添加字符。但是当我尝试设置代码片段时,我收到了另一条错误消息。
编辑 contenteditable
div:
在
storeIndexes
方法中,我为highlights
数组中的每个元素创建并存储范围。这个方法叫做@beforeinput
。此事件并非在所有浏览器中都可用,我使用的是 Chrome.接下来,我希望
contenteditable
div 中的文本得到更新。最后,范围应该通过称为
@input
的restoreIndexes
方法恢复。
我知道我的代码不应该有任何可见的效果。我的问题是尝试编辑文本时出现错误消息:Rangy warning: Module SaveRestore: Marker element has been removed. Cannot restore selection.
这是怎么回事?
new Vue({
el: '#app',
data: {
currentHighlights: [],
highlights: [
{
start: 10,
end: 20
}
],
},
methods: {
// What happens just before an edit is applied
storeIndexes: function(event) {
// Create a new range object
let range = rangy.createRange();
// Get contenteditable element
let container = document.getElementById('text-with-highlights');
// Store all currently highlights and addd DOM markers
this.highlights.forEach(highlight => {
// Move range based on character indexes
range.selectCharacters(container, highlight.start, highlight.end);
// Set DOM markers and store range
this.currentHighlights.push(rangy.saveRange(range))
});
},
// What happens after an edit was made
restoreIndexes: function(event) {
// Create a new range object
let range = rangy.createRange();
// Get range based on character indexes
let container = document.getElementById('text-with-highlights');
this.currentHighlights.forEach(highlight => {
range.selectCharacters(container, highlight.start, highlight.end);
rangy.restoreRange(range);
});
this.currentHighlights = [];
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-core.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-selectionsaverestore.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-textrange.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id='app'>
<div @beforeinput='storeIndexes' @input='restoreIndexes' contenteditable id='text-with-highlights'>
Just some text to show the problem.
</div>
</div>
事实证明这不是 Vue 问题,而是异步代码 运行 之一:restoreIndexes
尝试恢复范围时 storeIndexes
未完成。
setTimeout
成功了。我不确定是否有比将方法延迟一些随机间隔更好的方法,
// What happens after an edit was made
restoreIndexes: function(event) {
setTimeout(() => {
// Create a new range object
let range = rangy.createRange();
// Get range based on character indexes
let container = document.getElementById('text-with-highlights');
this.currentHighlights.forEach(highlight => {
range.selectCharacters(container, highlight.start, highlight.end);
rangy.restoreRange(range);
});
}, 10);
// Restore highlights
this.currentHighlights = [];
},
但是,我可以使用 v-runtime-template 库完全摆脱我的 storeIndexes
方法。这是 v-html
的替代方法,但也适用于以编程方式插入的元素,例如我的问题中的突出显示。
现在,我的高亮显示仅对 $data
中索引的变化做出反应,我不需要在 contenteditable
div 更新时手动移动它们。