Skip to content

Instantly share code, notes, and snippets.

@jonathantneal
Created November 8, 2011 05:18
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jonathantneal/1347070 to your computer and use it in GitHub Desktop.
Save jonathantneal/1347070 to your computer and use it in GitHub Desktop.
cross-browser-text-selection.js
(function (win, doc) {
function getRangeAtCollapse(range, collapsed) {
// get range as item
if (range.item) {
var rangeItem = range.item(0);
// return the data
return { node: rangeItem };
}
// get range as text
var
rangeA = range.duplicate(),
rangeB = range.duplicate(),
i = -1,
rangeElement, rangeNode, offset;
// collapse the range to the start or end of the selection
rangeB.collapse(!!collapsed);
// get the closest available element node
rangeElement = rangeB.parentElement();
// read between the element and the selection
rangeA.moveToElementText(rangeElement);
rangeA.setEndPoint('EndToStart', rangeB);
// get the offset despite a failure to read \r\n
offset = rangeA.text.replace(/\r\n/gm, '\n').length;
// get the offset between the textnodes
while (offset > -1 && i + 1 < rangeElement.childNodes.length) offset -= (rangeElement.childNodes[++i].nodeValue || rangeElement.childNodes[i].innerHTML).length;
// hey look, the text node
rangeNode = rangeElement.childNodes[i] || rangeElement;
// return the data
return {
node: rangeNode,
offset: String(rangeNode.nodeValue || rangeNode.innerHTML || rangeNode.value || '').length + offset
};
}
function getRangeFromDocumentSelection() {
var
range = doc.selection.createRange(),
rangeDataStart = getRangeAtCollapse(range, true),
rangeDataEnd = getRangeAtCollapse(range, false);
// return the data
return {
node: rangeDataStart.node === rangeDataEnd.node ? rangeDataStart.node : range.parentElement(),
start: {
node: rangeDataStart.node,
offset: rangeDataStart.offset
},
end: {
node: rangeDataEnd.node,
offset: rangeDataEnd.offset
}
};
}
function isNodeTextType(node) { return /[348]/.test(node.nodeType); }
win.textSelection = ('getSelection' in win) ? function () {
var winGetSelection = win.getSelection(), docActiveElement = doc.activeElement;
// get range as field
if ('selectionStart' in docActiveElement) {
// return the data
return {
node: docActiveElement,
start: {
node: docActiveElement,
offset: docActiveElement.selectionStart
},
end: {
node: docActiveElement,
offset: docActiveElement.selectionEnd
}
};
}
// get range as text
if (winGetSelection.rangeCount) {
var
range = winGetSelection.getRangeAt(0),
node = range.commonAncestorContainer,
start = {
node: range.startContainer,
offset: range.startOffset
},
end = {
node: range.endContainer,
offset: range.endOffset
};
// correct misguided offsets
if (!isNodeTextType(start.node) && start.offset > start.node.childNodes.length - 1) start.offset = start.node.innerHTML.length;
if (!isNodeTextType(end.node) && end.offset > end.node.childNodes.length - 1) end.offset = end.node.innerHTML.length;
// return the data
return { node: node, start: start, end: end };
}
} : ('selection' in doc) ? getRangeFromDocumentSelection : function() {};
})(this, document);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment