1

I've known how to use the document.selection to do the highlighting. For example

/* http://jsfiddle.net/4J2dy/ */
$("#content").on('mouseup', function() {
    highlighting();
});
var highlighting = function () {
    var seleted_str = (document.all) ? document.selection.createRange().text : document.getSelection();
    if(seleted_str != "") {
        var stringToBeHighlighted = seleted_str.getRangeAt(0);
        var span = document.createElement("span");
        span.style.cssText = "background-color: #80deea";
        span.className = "MT";
        stringToBeHighlighted.surroundContents(span);
    }
};

But there is something I don't know how to achieve. Let's say that I have four layers created with the same content at the same time. And I would like to select a sentence on the controlling layer while all the same sentence in the other three layers will be selected too.(See image below) Step1

After the selection, I would like to pop out a menu(which I can do), and get the DOM element based on which button is pressed.(See image below) Step2

Could anyone tell me how to achieve this? Or it just can't be done? I would be grateful if someone could answer for me.

1 Answer 1

1

It's kind of possible, and I would appreciate the input of SO user Tim Down as he knows a lot about JS Range/Selections, but I'll present my partial solution already.

Instead of selecting the 4 layers, you could just store the startOffset & endOffset in an external object that is updated on mouseup. The only by-effect this has, is that the user's selection will only get the color of the span when they click a layer button.

The advantage is that you can now simply work with DOM Textnodes as opposed to ranges/ selection (more complex, to me anyway). I've chosen to find the layers with a data-layer attribute on the buttons and a corresponding id on the layers themselves. I handled the 'appending' of the 'selected span' by slicing the text content of the text nodes in the layers, like so:

layer.innerHTML = txt.slice(0, selText.start) 
     + '<span class="MT" style="background-color: #80deea">'
     + txt.slice(selText.start, selText.end) + '</span>'
     + txt.slice(selText.end, txt.length);

See it in action here. I've added a cleanSelection function so only one selection is possible at a time (the start & end counters fail because selection ranges don't take into account HTML tags, so you have to get rid of the spans).

Final notes:

  • The fiddle will not work in browsers not supporting getElementsByClassName
  • The fiddle only supports one selection at a time.
  • The fiddle does not extensively test all conditions (eg, whether the nodetype of the first child is truly a text node, etc. But it ain't hard to add that yourself)

Entire JS code as reference (also in fiddle):

// this object will hold the start & end offsets of selection value
var selText = false; 

// selText will be updated on mouseup
document.body.onmouseup = getSelText; 

// on button click, selText will be highlighted
document.body.onclick = function(e) { 
  var target = e.target || e.srcElement, range, layer, txt;

  // only do if it's a layer button & the selection is non-empty
  if (target.getAttribute('data-layer') && selText !== false) {

     // first remove previous spans, they break the startOffset & endOffset of the selection range
     cleanSelection();

     // get the clicked layer
     layer = document.getElementById(target.getAttribute('data-layer'));
     // this assumes that the first node in the layer is a textNode
     txt = layer.firstChild.nodeValue;
     // let's append the selection container now
     layer.innerHTML = txt.slice(0, selText.start) 
     + '<span class="MT" style="background-color: #80deea">'
     + txt.slice(selText.start, selText.end) + '</span>'
     + txt.slice(selText.end, txt.length);

    // ...and empty the 'real selection'
    window.getSelection().collapse(); 
    // log results to console
    console.log('From char ' + selText.start + ' to char ' + selText.end + ', in ' + layer.id);
  }

};
function getSelText () {
    var seleted_str = (document.all) ? document.selection.createRange().text : document.getSelection(), stringToBeHighlighted;
    if(seleted_str !== "") {
        stringToBeHighlighted = seleted_str.getRangeAt(0);
        selText = {
          start: stringToBeHighlighted.startOffset,
          end: stringToBeHighlighted.endOffset
        };
    } else {
        selText = false;
    }
}
function cleanSelection() {
  var getText, mtSpan = document.getElementsByClassName('MT');
  for ( var i = 0; i < mtSpan.length; i++) {
    getText = mtSpan[i].innerHTML;
    mtSpan[i].previousSibling.nodeValue = mtSpan[i].previousSibling.nodeValue + getText + mtSpan[i].nextSibling.nodeValue;
    mtSpan[i].parentNode.removeChild(mtSpan[i].nextSibling);
    mtSpan[i].parentNode.removeChild(mtSpan[i]);
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Really thanks for the answer! Also thanks for the notes for saving my time on testing. Nice answer and the explanation =)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.