在 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:

时我预计会发生三件事

我知道我的代码不应该有任何可见的效果。我的问题是尝试编辑文本时出现错误消息: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 更新时手动移动它们。