Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS - surroundContents only retains highlight on text about 20% of the highlight attempts

I am using a mouseup event to trigger a function which highlights text and surrounds the highlighted text with a span (function from stack overflow):

function highlightText(e) {

    var t = window.getSelection().toString();
    if (t) {
        $("#mySpan").remove();
        var range   = window.getSelection().getRangeAt(0);
            newNode = document.createElement("span");
        newNode.id  = 'mySpan';

        range.surroundContents(newNode);

    }
}

The main problem I am encountering is that as long as surroundContents is included, the text remains highlighted only about 20% of the highlight attempts (otherwise highlighting disappears immediately). I tried adding a setTimeout, not calling surroundContent for 1s. I also tried removing the remove() statement, but still no good.

Any ideas on why this is happening?

like image 201
The One and Only ChemistryBlob Avatar asked May 26 '16 21:05

The One and Only ChemistryBlob


1 Answers

I was facing the same problem with Chromium on Android. In some specific cases, the call of range.surroundContents(newNode) would cause a very weird behaviour of page reload and so on. After checking the documentation of the function:

This method is nearly equivalent to newNode.appendChild(range.extractContents()); range.insertNode(newNode). After surrounding, the boundary points of the range include newNode.

So the obvious thing was to apply another way highlight the text. I found mark.js library which did exactly what I wanted without that annoying side effect. (Here's a JSFiddle sample that shows how it's used to highlight just selection). The difference is that library was not using range.surroundContents(newNode) nor newNode.appendChild but rather node.replaceChild.

Based on that, here's the solution to the problem I was having and I think it applies to your case as well.

function surroundRangeWithSpan(range) {
    var span = document.createElement('span');

    // The text is within the same node (no other html elements inside of it)
    if (range.startContainer.isEqualNode(range.endContainer) && range.startContainer.childNodes.length == 0) {
      // Here you customise your <span> element
      customSurroundContents(range, span);
    } else {
      // Here you have to break the selection down
    }
    return span;
  }

  function customSurroundContents(range, span) {
    var node = range.commonAncestorContainer;
    var startNode = node.splitText(range.startOffset);
    var ret = startNode.splitText(range.toString().length);
    span.textContent = startNode.textContent;
    startNode.parentNode.replaceChild(span, startNode);
  }

And you pass window.getSelection().getRangeAt(0) to the function.

like image 154
Sohayb Hassoun Avatar answered Sep 27 '22 22:09

Sohayb Hassoun