Danny Lin
Danny Lin

Reputation: 2300

Get source code fragment with range from a selection?

For example, if I have the HTML:

<div id="foo">
<p>Some text in a paragraph</p>
<p>More text in a paragraph</p>
</div>

And someone selects from the start of "in" (paragraph 1) to the end of "More" (paragraph 2), I want to get the selection info like:

{
    "object": [div #foo],  /* commonAncestorContainer DOM element of the selection */
    "source": "<div id="foo">\n<p>Some text in a paragraph</p>\n<p>More text in a paragraph</p>\n</div>",  /* outerHTML of the commonAncestorContainer */
    "startOffset": 28,  /* offset of selection starting point in the source code */
    "endOffset": 54  /* offset of selection ending point in the source code */
}

Here are some problems when I attempt to do this:

  1. We can use Range.commonAncestorContainer to get commonAncestorContainer of a range. However how do we get the real commonAncestorContainer if a selection contains multiple ranges?

  2. How to get the startOffset and endOffset of the selection range in the source code fragment?

Upvotes: 3

Views: 654

Answers (2)

ViRa
ViRa

Reputation: 714

  1. You may want to check out a related question on finding common ancestors in stack overflow. When the selection contains multiple ranges, you could use the common ancestor algo to the get the common ancestor of all the range.commonAncestorContainer.

  2. Here is a demo of the code to get the start and end offset within the source. You may want to test and extend it as needed.

    function getPosition(node, child, childOffset) {
        if (!node.contains(child)) {
            return -1;
        }
        var children = node.childNodes;
        var pos = 0;
        for (var i = 0; i< children.length; i++) {
            if (children[i] === child) {
                pos += childOffset;
                break;
            } else if (children[i].contains(child)) {
                pos += getPosition(children[i], child, childOffset);
                break;
            } else if (children[i].nodeName === "#text") {
                pos += children[i].textContent.length;
            } else {
                pos += children[i].outerHTML.length;
            }
        }
        if (node.nodeName !== "#text") {
            pos += node.outerHTML.lastIndexOf(node.innerHTML);
        }
        return pos;
    }
    

Upvotes: 3

Khalid
Khalid

Reputation: 4798

Try this function returning an object of information you need

function getInfo(selector)
{
    var element = $(selector);
    var html = element.clone().wrap('<p>').parent().html();
    var Result = new Object();
    Result.object = "[" + element.prop("tagName") + " " + selector + "]";
    Result.source = html;
    Result.startOffset = $("body").html().indexOf(html);
    Result.endOffset = Result.startOffset + html.length;
    return Result;
}

The argument is the selector so you need to call the function like this:

var info = getInfo("#foo");

Upvotes: 0

Related Questions