"Notification: The marker element has been eliminated" encountered while attempting to restore text ranges using Rangy within a Vue component

I have a rather intricate Vue component that includes a contenteditable div. My goal is to highlight words within this div using the Rangy library and add extra markup while retaining this markup even after editing the text.

Initially, I planned on asking for help because adding extra markup seemed to render the contenteditable div uneditable; I couldn't delete or insert characters. However, as I attempted to set up a code snippet, another error surfaced.

When editing the contenteditable div, three main actions are expected:

  • In the storeIndexes method, ranges are created and stored for each element in the highlights array. This process occurs during the @beforeinput event which might not be supported by all browsers (I am using Chrome).

  • Following that, the text content inside the contenteditable div should be updated.

  • Lastly, the saved ranges should be restored through the restoreIndexes method triggered by the @input event.

Although my code isn't supposed to visibly alter anything, an error message pops up when attempting to edit the text:

Rangy warning: Module SaveRestore: Marker element has been removed. Cannot restore selection.

What seems to be the issue here?

new Vue({

  el: '#app',
  
  data: {
    currentHighlights: [],
    highlights: [
      { 
        start: 10,
        end: 20
      }
    ],
  },
  
  methods: {
    // Actions before applying an edit
    storeIndexes: function(event) {
      // Create a new range object
      let range = rangy.createRange();

      // Get the contenteditable element 
      let container = document.getElementById('text-with-highlights');

      // Store all existing highlights and add DOM markers
      this.highlights.forEach(highlight => {
        // Adjust the range based on character indexes
        range.selectCharacters(container, highlight.start, highlight.end);
        // Set DOM markers and save the range
        this.currentHighlights.push(rangy.saveRange(range))
      });
    },
    
    // Actions after making an edit
    restoreIndexes: function(event) {
      // Create a new range object
      let range = rangy.createRange();

      // Retrieve 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>

Answer №1

It turns out the issue was not with Vue, but rather a problem of code running asynchronously: storeIndexes had not finished when restoreIndexes attempted to restore ranges.

Using setTimeout resolved the issue for me. Although I'm uncertain if there is a more efficient way than simply delaying the method by a random interval,

// Handling post-edit actions
restoreIndexes: function(event) {
  setTimeout(() => {
    // Creating a new range object
    let range = rangy.createRange();

    // Obtaining 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);

  // Restoring highlights
  this.currentHighlights = [];
},

However, I managed to eliminate my storeIndexes method entirely by utilizing the functionality provided by the v-runtime-template library. This serves as an alternative to v-html, enabling programmatically inserted elements like the highlights in my scenario.

Now, my highlights automatically respond to changes in indexes within $data, mitigating the need for manual adjustments when the contenteditable div is updated.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When providing the index.html file using express.js, remember to include the user-agent header

When using my Express.js app to render my index.html page, I want to customize the http 'User-Agent' header. </p> <p>I've tried this method without success:</p> <pre><code>req.headers['user-agent'] = ...

What is the method for executing a function enclosed within a variable?

As someone new to the world of Java, I have encountered a puzzling issue with some code related to a game. Specifically, there seems to be an obstacle when it comes to utilizing the navigator function. When I click on this function in the game, some sort o ...

Challenge encountered while processing JSON data

I've searched through various threads and tutorials, but I'm still stuck on this issue! I have two JSON responses that need to be utilized in separate functions: 1st (single element) Need to extract "Intention_category" and "count" Data: { " ...

Is it possible to execute a program on MacOS through a local HTML website?

Are there any straightforward methods to launch Mac programs using HTML? I've created an HTML page featuring a text field and several buttons. The goal is for users to enter a code (numbers) that will then be copied to the clipboard. By clicking on t ...

Building a personalized django widget to enhance functionality on other websites

Currently, I am in the process of developing a new website that includes user statistics. My goal is to create a widget that can be embedded on other websites using JavaScript to pull data from my server and display the statistics for a specific user. Howe ...

What are the compatibility considerations for npm packages with Angular 2? How can I determine which packages will be supported?

When working with Angular 2, do NPM packages need to be modified for compatibility or can any existing package work seamlessly? If there are compatibility issues, how can one determine which packages will work? For instance, let's consider importing ...

Retrieve information from a pair of models

Hey there, I need some help. Can someone please guide me on how to obtain the 'topics' array and append it to res.view()? I've tried multiple approaches but keep getting 'undefined' in the 'topics' array. Subjects.qu ...

What is the process for adding an additional level to an Object for an item that is not predefined?

The primary concern at hand is as follows: Retrieve JSON data from the server Populate a form with the data Serialize the form Create a JSON object with the correct structure Send the JSON object back to the server I am facing challenges specifically on ...

Issue with VueJS and Jest: Module 'babel-core' not found

While attempting to integrate Jest with VueJS, I encountered an issue: Cannot find module 'babel-core' at Object.<anonymous> (node_modules/vue-jest/lib/compilers/babel-compiler.js:1:15). To resolve this, I had to include "@babel/core": " ...

Use jQuery to open and close HTML tags efficiently

It seems like the task I have at hand may not be as simple as I had hoped, so here I am seeking some reassurance. I am aiming to switch out an image with a closing div, then the image itself, followed by another opening div. Let me illustrate this with a ...

How to print a specific div from an HTML page with custom dimensions

I am looking for a solution to print just a specific div from a website with dimensions of 3"x5". Despite setting up the print button, the entire page continues to print every time. Is there a way to hide all non-div content in print preview? CSS .wholeb ...

Merge two arrays of the same size to create a single array of strings

Looking to merge the values of two equal-sized arrays and create a third array like the one shown below. I'm in need of guidance as I have not been able to locate a built-in JavaScript method for this specific task. The goal is to construct an array o ...

Simultaneously activate the 'onClick' and 'onClientClick' events on an ASP button using JavaScript

I have encountered an ASP button while working on existing code that has both onClick and onClientClick events attached to it. My goal is to trigger both events by generating a click event from an external Javascript file. The line of code I am using for ...

Ways to make the background color white in Bootstrap 5

Can someone assist me in changing the background color of my portfolio to white? I attempted to use global CSS, but the black background on both sides of the page is preventing the change. return ( <> <Navbar /> <main className= ...

What is the best way to send a value through an AJAX request?

My ajax update function is not working in the code below. How can I solve this issue? The edit method in my code is functioning properly. For example, the value is being passed in this code snippet: var name = row.find(".ContactPersonName").find("span"). ...

Using b-icon with the <td> tag in VueJS

I am looking to incorporate HTML content within a table data element using VueJS. Below is an illustration of my situation: <template> <div> <div v-if="someObject.properties" style="margin-top: 20px;" class="table-responsi ...

Utilize AngularJS to integrate a service into the router functionality

What is the best way to inject a service into my router so that its JSON result will be accessible throughout the entire application? Router: export default ['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterP ...

Using maxDate in Material UI DatePicker Component to set a maximum date limit

I'm having a tough time getting the maxDate property to function properly on the Material UI DatePicker component. It should disable dates after the specified maxDate. In my situation, I needed to set the maxDate to +60 days from the current Date(), ...

When attempting to register a custom Gamepad class using GamepadEvent, the conversion of the value to 'Gamepad' has failed

I have been working on developing a virtual controller in the form of a Gamepad class and registering it. Currently, my implementation is essentially a duplicate of the existing Gamepad class: class CustomController { readonly axes: ReadonlyArray<nu ...

When attempting to install material UI in my terminal, I encounter issues and encounter errors along the way

$ npm install @material-ui/core npm version : 6.14.4 Error: Source text contains an unrecognized token. At line:1 char:15 $ npm install <<<< @material-ui/core CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException ...