Alex
Alex

Reputation: 21

javascript selection across nodes: how to retrieve those nodes?

consider the following selected html snippet:

<span>...</span><span>...</span><span>....</span>
.......||||||||||||||||||||||||||||||.......

The second line represents the user selection (the pipes), spanning across several span tags. Using javascript, I'd like to retrieve the (non-text) nodes partially or completely selected (in this example the 3 span tags).

Thanks,

Upvotes: 2

Views: 2674

Answers (3)

7vujy0f0hy
7vujy0f0hy

Reputation: 9119

Solution: window.getSelection().getRangeAt(0).cloneContents()
Source: somebody’s deleted comment @ https://stackoverflow.com/questions/43542742/find-text-on-page-and-select-it

Basic example

window.getSelection().addRange(new Range());
var dispray = document.querySelectorAll("[id^=dispray]");
var Go = () => {
    var fragm = window.getSelection().getRangeAt(0).cloneContents();
    dispray[0].innerHTML = "";
    dispray[0].appendChild(fragm);
    dispray[1].innerText = dispray[0].innerHTML;
}
#sample {color: red;}
#sample > span {color: green;}
#panel {height: 5em; border: 3px solid #ccc; resize: vertical; overflow-y: scroll;}
#panel > table {width: 100%; height: 100%; /* border-collapse: collapse; */}
#panel > table td {vertical-align: top; padding: 0.5em;}
#panel > table #disprayA {border-right: 1px solid #ccc;}
<p id="sample" onMouseUp="Go()"><span>Lorem ipsum <b>dolor sit</b> amet.⇥</span
> Text node <span>⇤Maecenas <b>porttitor a felis</b> in pharetra.⇥</span><span
>⇤Nulla accumsan auctor est sit amet finibus.</span></p>

<div id="panel"><table><tr>
<td id="disprayA">Copied selection goes here...</td>
<td><code id="disprayB">Source code of copied selection goes here...</code></td>
</tr></table></div>

Complete example – no bare Text nodes

Your “little” requirement that copy of selection contain no bare Text nodes complicates the code greatly.

window.getSelection().addRange(new Range());
var dispray = document.querySelectorAll("[id^=dispray]");
var Go = () => {
    dispray[0].innerHTML = "";
    var r = window.getSelection().getRangeAt(0);
    var fragm = r.cloneContents();
    if ([...fragm.childNodes].some(child => child.nodeType === Node.TEXT_NODE)) {
        /* if selection contains some bare Text nodes... */
        var ancestorElement = r.commonAncestorContainer;
        if (ancestorElement.nodeType === Node.TEXT_NODE)
            ancestorElement = ancestorElement.parentElement;
        var ancestorElement2 = ancestorElement.cloneNode(false);
        ancestorElement2.appendChild(fragm);
        dispray[0].appendChild(ancestorElement2);
    }
    else dispray[0].appendChild(fragm); 
    dispray[1].innerText = dispray[0].innerHTML;
}
#sample {color: red;}
#sample > span {color: green;}
#panel {height: 5em; border: 3px solid #ccc; resize: vertical; overflow-y: scroll;}
#panel > table {width: 100%; height: 100%; /* border-collapse: collapse; */}
#panel > table td {vertical-align: top; padding: 0.5em;}
#panel > table #disprayA {border-right: 1px solid #ccc;}
<p id="sample" onMouseUp="Go()"><span>Lorem ipsum <b>dolor sit</b> amet.⇥</span
> Text node <span>⇤Maecenas <b>porttitor a felis</b> in pharetra.⇥</span><span
>⇤Nulla accumsan auctor est sit amet finibus.</span></p>

<div id="panel"><table><tr>
<td id="disprayA">Copied selection goes here...</td>
<td><code id="disprayB">Source code of copied selection goes here...</code></td>
</tr></table></div>

Upvotes: 3

Tim Down
Tim Down

Reputation: 324607

To do this cross browser (including IE) and quite conveniently, you can use my Rangy library, which provides extended DOM Range and Selection objects in all browsers. The code would look something like:

var sel = rangy.getSelection();
if (sel.rangeCount) {
    var range = sel.getRangeAt(0);
    var selectedElements = range.getNodes([1]); // [1] is an array of node types
    alert("Found " + selectedElements.length + " selected elements");
}

Upvotes: 2

jordancpaul
jordancpaul

Reputation: 2964

Unfortunately, there is no instant solution for that problem. To solve this you will need to use var sel = window.getSelection() to get an instance of a Selection object which represents the selected elements. From there, you can use sel.anchorNode() to get the element where the selection starts and sel.focusNode() to get the element where it ends. Keep in mind, these will be instances of TextNode and will actually be children of the span that you want.

You will have to come up with a way to determine the shortest path between the two nodes (anchor and focus) which will give you all the information you need.

EDIT: Oh, btw, this is the mozilla/webkit implementation. IE is a completely different beast. You will need to use var sel = document.selection. Check out How can I get the DOM element which contains the current selection? for more information on how to do this in a cross browser way

Upvotes: 0

Related Questions