Search code examples
jqueryhtmlrangy

Highlight specific parent nodes using Rangy library


I am using Rangy Library for highlighting text. When I use the following code, it simply add a span tag with the class note if there are no tags and while I am selecting the whole text in between a span tag it simply assign that class to the existing tag using highlighter.highlightSelection("note");.

Now my requirement is, rather than highlighting the selected text, i need to highlight the whole text of its parent node. As you can see in the following snippet, I am having a span tag with s-class sentence which is again made of using one or multiple child span tag. Now I want, if user select some text from the child span tags than the parent tag with attribute s-class=sentence should be highlighted. If user is trying to select the text across the multiple parent span tags then all the parent tags affected with this selection should be highlighted.

<p>
<span s-class="sentence">
<span>Contrary to popular belief, Lorem Ipsum is not simply random text. </span>
</span>
<span s-class="sentence">
<span>It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. </span>
</span>
<span s-class="sentence">
<span>Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. </span>
</span>
<span s-class="sentence">
<span>Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. </span>
</span>
<span s-class="sentence">
<span>This book is a treatise on the theory of ethics, very popular during the Renaissance. </span></span>
<span s-class="sentence">
<span>The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet.."</span><span >, comes from a line in section 1.10.32.</span>
</span>
</p>

Use case 1 => User selected the test from first span only "popular belief, Lorem", then the highlighted html should be -

<span s-class="sentence" class="note">
<span>Contrary to popular belief, Lorem Ipsum is not simply random text. </span>
</span>

Use case 2 => If selects from two or more spans(first and second) "random text. It has roots" then the resultant highlighted html should be -

<span s-class="sentence" class="note">
<span>Contrary to popular belief, Lorem Ipsum is not simply random text. </span>
</span>
<span s-class="sentence" class="note">
<span>It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. </span>
</span>

Use case 3 - If user select some text across multiple child elements inside a parent tag with attribute s-class-"senetence" then only that parent tag needs to be highlighted. i.e. user selects "sit amet.., comes" then the resultant HTML should be -

<span s-class="sentence" class="note">
<span>The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet.."</span><span >, comes from a line in section 1.10.32.</span>
</span>

I am converting this HTML from a Word document and then making the sentences so I wants to select all the affected sentences by the selection not only the highlighted text.

Also when I unhighlight it then the selection should be removed from these parents spans.


Solution

  • This should work for you.

    var selection = rangy.getSelection();
            if (selection.rangeCount > 0) {
                var range = selection.getRangeAt(0);
                var startNode = $(range.startContainer).closest("span[s-class=sentence]");
                var endNode = $(range.endContainer).closest("span[s-class=sentence]");
                $(startNode).addClass("selectionstart");
                $(endNode).addClass("selectionend");
                var rangeSel = $('span').rangeSelector();
                if(rangeSel!==undefined && rangeSel.length>0){
                    $(startNode).addClass('note');
                    $(endNode).addClass('note');
                    rangeSel.addClass("note"); 
    }}
    //jquery extension method
    $.fn.rangeSelector = function (options) {
        var settings = $.extend({ startClass: 'selectionstart', endClass: 'selectionend' }, options || {});
        var name = 'span';
        var startNode = name + '.' + settings.startClass;
        var endNode = name + '.' + settings.endClass;
        var parentNode = $(startNode).parent().closest("p");
        var startIndex = parentNode.find(startNode).index();
        var endIndex = parentNode.find(endNode).index();
        if ((startIndex === endIndex) || (endIndex === startIndex+1))
            return $('<span />');
        if (endIndex < 0)
            return undefined;
        var result = $(startNode).nextUntil(endNode);
        return result;
    }