How can Vue allow for text selection to be encapsulated within a span element with two-way binding for styling

I have developed an application that allows users to highlight text with different colors while reading.

For this functionality, I am utilizing the Selection API. However, I am encountering issues with the two-way binding aspect. When a user changes the color, the highlighted text does not update simultaneously.

Is there anyone who can provide assistance? http://jsfiddle.net/eywraw8t/424087/

To clarify my question, I am looking for the functionality where if a user changes the color, all previously highlighted text will also update.

Here is a step-by-step reproduction of the issue:

  1. Default color #ccc

  2. Highlight some text (it's gray)

  3. Choose another color, for example #ff0000 (now any new highlighted text will be red, but previously highlighted text remains gray because the span is created via createElement, not bound to Vue's data)

HTML:

<div id="app">
  <label v-for="color in colors">
    <input type="radio" v-model="currentColor" :value="color">
    {{color}}
  </label>
  <h4>Select Text To Highlight</h4>
  <p id="text" @mouseup="highlight">
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatum ducimus veniam esse, nam rerum cumque repellat maiores! Explicabo laudantium, magni dignissimos impedit labore, dolores deserunt aspernatur eos quo, vero consequatur.
  </p>
</div>

JS:

new Vue({
  el: "#app",
  data: {
    currentColor:'#ccc',
    colors: ["#ccc","#ff0000","#00ff00","#0000ff"],
    selectedRange: null
  },
  methods: {
    getSelectedText() {
      if (window.getSelection) {
        let sel = window.getSelection()
        if (sel.getRangeAt && sel.rangeCount) {
          this.selectedRange = sel.getRangeAt(0);
        }
        return window.getSelection().toString()
      } else if (document.selection) {
        this.selectedRange = document.selection.createRange()
        return document.selection.createRange().text
      }
      return ''
    },
    surroundSelection(color) {
      var span = document.createElement("span");
      span.className = 'highlight'
      span.style.fontWeight = "bold"
      span.style.color = color
      span.addEventListener("click", () => {
        console.log('click');
      });
      if (window.getSelection) {
        var sel = window.getSelection()
        if (sel.rangeCount) {
          var range = this.selectedRange.cloneRange()
          range.surroundContents(span)
          sel.removeAllRanges()
          sel.addRange(range)
        }
      }
    },
    highlight(){
      let text = this.getSelectedText();
      if (this.selectedRange && text) {
        this.surroundSelection(this.currentColor)
      }
    }
  }
})

Answer №1

To address this issue, you could consider implementing a watcher for 'currentColor'. This way, the color will only be applied to 'span.style.color' when the selection changes.

new Vue({
  el: "#app",
  data: {
    currentColor:'#ccc',
    currentSpan: null,
    colors: ["#ccc","#ff0000","#00ff00","#0000ff"],
    selectedRange: null
  },
  watch: {
    currentColor: function (newColor, oldColor) {
        if (!this.currentSpan) {
          return;
        }

        this.currentSpan.style.color = newColor;
    }
  },
  methods: {
    ...
    surrountSelection: function (color) {
      ...
      return span;
    },
    highlight: function () {
      if (this.selectedRange && text) {
        this.currentSpan = this.surroundText(this.currentColor);
      } else if (this.currentSpan) {
        this.currentSpan = null;
      }
    }
  }
}

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

Transmitting C# data to a JavaScript script through JSON serialization. Issue encountered: Character invalid

I'm facing an issue with sending data from my Entity Framework database to a JavaScript script on my webpage. Below is the snippet of code from my MVC Controller: public ActionResult Index() { var wordsToShow = db.Words.Where(w => w.O ...

Begin anew with Flip.js

Currently, I am incorporating the jquery flip plugin from nnattawat.github.io/flip to establish a card flipping mechanism on my website. I have successfully implemented the method outlined in the documentation to unregister the flip event from the elemen ...

Resizing a column to match the dimensions of a grid of pictures

Imagine you have a website structured like this. #left_column { width: 200px; } <div id="left_column"> /* some content */ </div> <div id="right_column"> /* A series of photos each with a width of 100px and floated */ </div> In t ...

What is the best way to calculate the number of days that have passed since a certain

Hey there, I've got this 10 Dec, 2019T14:07:21 date format from the backend. My goal is to determine how many days ago it was. For example, today is the 20th, so if the date is today's date, it should show as 0 days ago. ...

When attempting to retrieve a data object using a Vuex action, I encounter an "Uncaught (in promise) TypeError" when trying to access the data within the object

Recently, I've been diving into Vuex and its actions for fetching data. While everything seems to be working smoothly - accessing the films object, selecting films from it - I encounter an error when trying to access specific data within a film. Vuex ...

Is the `document.documentElement` consistently defined and always representing the HTML element?

I am looking to make changes to the <html> element using a script within the <head> section of an HTML page. My goal is to only access and modify the <html> element itself, without affecting any of its children. Should I wait for the DOM ...

Saving an array of key-value pairs in local storage

I'm attempting to save an array in local storage using the following code: var tempval = []; tempval['key'] = 1; localStorage.setItem("Message", JSON.stringify(tempval)); However, when I check local storage, it only sho ...

Updating the background-image of an element in a v-for loop

Currently, I have a situation where my code is using v-for to read various .md files that contain an image property in their front matter. The challenge I am facing is altering the background-image of my div based on the URL extracted from each file' ...

Secret key management in VueJs: Best practices

As I prepare to launch my personal blog, built using VueJS and hosted by a Go web-server, I am faced with the challenge of securely storing private keys. The content for my blog is managed by ButterCMS and accessed through the fetch API. During developmen ...

Learn the method of showcasing a JavaScript variable value within an HTML href tag

Currently, I am in the process of rewriting my URL and category name to be passed into the URL. For this purpose, I am creating a URL friendly string using the following JavaScript function: function getUrlFriendlyString(str) { // convert spaces to &ap ...

What is the best way to create a form that includes both dynamic objects and dynamic arrays using a JSON schema?

I have observed how a JSON schema can be utilized to construct dynamic arrays. My goal is to develop a JSON web form using a JSON schema that allows for objects (dictionaries) to be expandable similar to arrays. For example, you can visit the demonstrati ...

Sending data to API using AngularJS Http Post

Upon clicking "Add new User", a modal pop-up will appear with a form containing a text field and a checkbox. However, upon clicking the create button, the data is not being posted to the API and the modal pop-up remains open without closing. I would like ...

Please refrain from including HTML code within the div element

When I input the following text inside a textarea: <strong>test</strong> and then display it within a div, the text appears in bold instead of showing the HTML code as entered. How can I prevent the HTML code from being rendered and instead d ...

Can README docs be prioritized to appear before all other stories in Storybook navigation?

Organizing my Storybook stories is important to me. I like to nest all my stories under a “Docs” header, with each component having a README mdx file followed by its stories. My preferred order is to always have the README appear first in the navigatio ...

Position divs to be perfectly aligned and centered

I'm in the process of creating a webpage and facing challenges with aligning my divs properly, specifically the animated company logos. I want them to be positioned side by side rather than on top of each other, with space in between to fit the layou ...

The ng-model failed to display the updated value until a click was made somewhere on the page

I am struggling with displaying the correct value of an ngModel variable defined in my controller. Despite changing to the correct value in the console, it doesn't update on the page until I click somewhere else or hit the update button again. Here&a ...

The JavaScript code is not being executed, as the address bar displays the function name instead

In my project, I have created numerous "js-classes" with various functions spread across different files. Unfortunately, the codebase is too large to share entirely. However, towards the end of the project, I encountered a bug where a specific function wa ...

In Node.js, while running unit tests, the importing function is limited to read-only access

Having trouble mocking an async function in Jest? I followed the documentation and used mockResolvedValue, but encountered a read-only issue when trying to import my mock function from another file. Check out my code below: //index.js async function get ...

Javascript - issue with accurately retrieving element offset value while scrolling

My goal is to dynamically change classes for elements based on their position during scrolling. I am trying to calculate the top offset element value and adjust the classes accordingly. Here is the function I am using: handleScroll () { const header = ...

What is the ideal way to utilize Vue within the framework of multiple pages?

When using the default Vue CLI setup, Vue is loaded from the index.html page. To work with Vue and dynamic content on the page, everything is typically handled in the App.vue file. But what if you want to make significant layout changes to the page? How d ...