Search code examples
javascriptrangy

Split rangy range into two at specific offset


Ok, have been breaking my head on this one for the last few hours and hope there is somebody here with more experience with the rangy library. Let's say I have the following html:

<p>Lorem Ipsum<note>that's latin</note> is a placeholder text</p>
<p>It is often used in graphic design<note>some other note</note></p>

And we've got a selection as indicated by the { and } characters:

<p>Lorem {Ipsum<note>that's latin</note> is a placeholder text</p>
<p>It is often used in graphic design<note>some other note</note>.}</p>

How could you split up the range into 3 range objects like this:

  1. Ipsum
  2. is a placeholder text</p><p>It is often used in graphic design
  3. .

With the goal being to exclude a set of elements, in this case being ['note']. I don't require a fully functional solution, all I am looking for is a way to 'split' a rangy range up at a certain point in the range.


Solution

  • This isn't really specific to Rangy since you'll just use standard Range methods. Here's a simple function that will work on regular DOM ranges or Rangy ranges. It splits a range in two in a similar way to the splitText() method of text nodes: the original range's end boundary is moved to the split point and the function returns a new range starting at the split point and ending at the original range's original end boundary.

    function splitRange(range, node, offset) {
        var newRange = range.cloneRange();
        newRange.setStart(node, offset);
        range.setEnd(node, offset);
        return newRange;
    }
    

    You could add this a method to all Rangy ranges if you like:

    rangy.rangePrototype.splitRange = function(node, offset) {
        var newRange = this.cloneRange();
        newRange.setStart(node, offset);
        this.setEnd(node, offset);
        return newRange;
    };
    

    You could then split your example selection as follows:

    // Get the <p> elements in the example HTML
    var allPs = document.getElementsByTagName("p");
    
    var range1 = rangy.getSelection().getRangeAt(0);
    
    // Split after the first child of the first <p>
    var range2 = range1.splitRange(allPs[0], 1); 
    
    // Split after the first child of the second <p>
    var range3 = range2.splitRange(allPs[1], 1);
    

    range1, range2 and range3 are now as you require.