This certainly isn't a trivial task, since you're faced with the two major problems of locating the text in the current document, and then being able to find it again on a subsequent page load. The problem is further complicated if the content of your page is subject to change, since you can't even rely on the relative position of the text to stay the same.
You may want to consider whether or not this is the best approach for whatever you're trying to accomplish given the effort required, but here's something that might get you started in the right direction:
function getSelection() {
var selection, position;
if (window.getSelection) {
selection = window.getSelection();
if (selection && !selection.isCollapsed) {
position = {
'offset': selection.anchorOffset,
'length': selection.toString().length,
// We're assuming this will be a text node
'node': selection.anchorNode.parentNode
};
}
} else if (document.selection) {
selection = document.selection.createRange();
if (selection && selection.text.length) {
var text = selection.parentElement().innerText,
range = document.body.createTextRange(),
last = 0, index = -1;
range.moveToElementText(selection.parentElement());
// Figure out which instance of the selected text in the overall
// text is the correct one by walking through the occurrences
while ((index = text.indexOf(selection.text, ++index)) !== -1) {
range.moveStart('character', index - last);
last = index;
if (selection.offsetLeft == range.offsetLeft && selection.offsetTop == range.offsetTop) {
break;
}
}
position = {
'offset': index,
'length': selection.text.length,
'node': selection.parentElement()
};
}
}
return position;
}
As well as a method to select the text again:
function setSelection(position) {
if (!position || !position.node) {
return;
}
var selection, range, element;
if (document.createRange) {
element = position.node.childNodes[0];
range = document.createRange();
range.setStart(element, position.offset);
range.setEnd(element, position.offset + position.length);
selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
} else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(position.node);
range.collapse(true);
range.move('character', position.offset);
range.moveEnd('character', position.length);
range.select();
}
}
This code makes the rather naïve assumption that the selected text all resides in the same DOM element. Chances are high that if a user is selecting arbitrary text this won't be the case.
Given that the Selection
object accounts for this with the anchorNode
and focusNode
properties, you could try and work around this, though dealing with the TextRange
object in Internet Explorer might prove to be a bit more problematic.
There's also the problem of how to keep track of the position.node
value across page requests. In my jsFiddle sample, I've used a slightly modified version of a selector-generating jQuery function to generate a selector string that can be saved and used to reselect the correct node later on. Note that the process is relatively trivial, so you could easily do it without jQuery – it just happened to save some effort in this case.
Of course if you're changing the DOM between visits, this approach will likely be fairly unstable. If you aren't though, I feel like it's probably one of the more reliable options.