My Rangy library will get your part of the way there by unifying the different APIs in IE < 9 and all other major browsers, and by providing a getNodes()
function on its Range objects:
function getSelectedNodes() {
var selectedNodes = [];
var sel = rangy.getSelection();
for (var i = 0; i < sel.rangeCount; ++i) {
selectedNodes = selectedNodes.concat( sel.getRangeAt(i).getNodes() );
}
return selectedNodes;
}
Getting the selected text is pretty easy in all browsers. In Rangy it's just
var selectedText = rangy.getSelection().toString();
Without Rangy:
function getSelectedText() {
var sel, text = "";
if (window.getSelection) {
text = "" + window.getSelection();
} else if ( (sel = document.selection) && sel.type == "Text") {
text = sel.createRange().text;
}
return text;
}
As for the character offsets, you can do something like this for any node node
in the selection. Note this does not necessarily represent the visible text in the document because it takes no account of collapsed spaces, text hidden via CSS, text positioned outside the normal document flow via CSS, line breaks implied by <br>
and block elements, plus other subtleties.
var sel = rangy.getSelection();
var selRange = sel.getRangeAt(0);
var rangePrecedingNode = rangy.createRange();
rangePrecedingNode.setStart(selRange.startContainer, selRange.startOffset);
rangePrecedingNode.setEndBefore(node);
var startIndex = rangePrecedingNode.toString().length;
rangePrecedingNode.setEndAfter(node);
var endIndex = rangePrecedingNode.toString().length;
alert(startIndex + ", " + endIndex);