javascript - 在 Vue 组件内使用 Rangey 恢复文本范围时出现错误消息 "Marker element has been removed"

标签 javascript vue.js vuejs2 contenteditable rangy

我有一个相当复杂的 Vue 组件,其中涉及一个 contenteditable div。我想使用 Rangy 突出显示此 div 中的单词并添加其他标记,即使在编辑文本时也保留此标记。

最初,我打算发布一个问题,因为在某些时候处理额外的标记会使 contenteditable div 不可编辑,我只是​​无法删除或添加字符。但是当我尝试设置代码片段时,我收到了另一条错误消息。

编辑 contenteditable div 时,我预计会发生三件事:

  • storeIndexes 方法中,我为 highlights 数组中的每个元素创建并存储范围。该方法称为@beforeinput。此事件并非在所有浏览器中都可用,我使用的是 Chrome。

  • 接下来,我希望更新 contenteditable div 内的文本。

  • 最后,应通过名为 @inputrestoreIndexes 方法恢复范围。

我知道我的代码不应该有任何可见的效果。我的问题是,在尝试编辑文本时出现错误消息: Rangy warning: Module SaveRestore: Marker element has been returned。无法恢复选择。

这里出了什么问题?

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 中的索引变化使用react,并且在更新 contenteditable div 时我不需要手动移动它们。

关于javascript - 在 Vue 组件内使用 Rangey 恢复文本范围时出现错误消息 "Marker element has been removed",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57695397/

相关文章:

javascript - 在点击的 <li> 中获取最近的 div

Javascript 用逗号将计数添加到数字中

webpack - 提取 CSS 用于 SSR (Vue.js)

javascript - Vue 将变量与兄弟组件数据进行比较

javascript - 在 Vue.Js 中的 v-text 中附加多个字段

javascript - 如何在点击时将每个可选择的 li 设置为不同的颜色? (JQuery 用户界面)

vue.js - 将双向绑定(bind) Prop 传递给插槽

javascript - 在 Vue.js 中操作数据值

javascript - 将正则表达式添加到 Vue.js 数据对象

javascript - 使用 Jquery 防止在 html() 期间创建 html 标签