0

I'm developing an e-learning application. In one exercise, I'd like to have our students read a text, flag mistakes that I put in it and evaluate their response.

Using Rangy, I solved some problems concerning text selection (let's say there's a <p id='myText'> containing the text to be corrected):

  • Users can mark text in myText on click. I say mark because although I do use the Selection API in the process, in the end it's about a kind of selection that's more persistent.
  • Markings always expand to word level. Each click marks a single word.
  • Any number of words between 0 and n (where n is the number of words in myText.innerText) can be marked.
  • Each marked word is styled using a span with a highlight class.

I do all of this using Vue in a few lines of codes. This function is triggered whenever I click on myText:

highlight: function() {
    // Get Selection
    let selection = this.$rangy.getSelection();

    // Some RegEx checking omitted

    // Expand selection to word boundaries
    selection.expand('word');

    // Some more RegEx checking omitted

    // A range represents a continuous part of selected text
    let range = selection.getRangeAt(0);

    // Highlight text or remove marking
    let applier = this.$rangy.createClassApplier('highlight');
    applier.toggleRange(range);
}

I need to compare the marked words against a list of words that actually contain mistakes, so I tried to store the indexes of each selected range. But it seems there's no function in any Browser API or in Rangy that gives me the indexes relative to the text itself, I can only get indexes relative to the HTML in myText - which is changing with each marking or unmarking as Rangy inserts spans to highlight the words that the user selects.

Is there an elegant solution to my problem? I could store each marked word with it's left and right neighbours (text between the word and the boundaries of myText) and compare it to a text-only version of myText's contents, but that seems rather clumsy. Is there a better way?

isherwood
  • 58,414
  • 16
  • 114
  • 157
Pida
  • 928
  • 9
  • 32

1 Answers1

0

There is a highlighter module in Rangy that I think sounds like what you want. Rangy also has various APIs for returning ranges and selection relative to the text within a page. Hopefully the documentation will help and I can answer questions arising.

Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 1
    I had already been using the highlighter module. What I needed is `toCharacterRange(Node containerNode)` from the Text Range Module. I just found this method in [another answer of yours](https://stackoverflow.com/a/10960441/2487047), not sure how I could miss it before as the docs are very clear on this: _Returns the range as a pair of character indices relative to the start of the visible text_. Thank you for making Rangy! – Pida Mar 22 '19 at 15:22
  • @Pida: Great. There is also the [`getBookmark()`](https://github.com/timdown/rangy/wiki/Rangy-Range#getbookmarknode-containernode) method of Rangy's range if you don't want the slowness of the TextRange module's `toCharacterRange()` method and don't need to worry about ignoring white space text nodes etc. – Tim Down Mar 22 '19 at 15:27