Created
November 8, 2011 05:18
-
-
Save jonathantneal/1347070 to your computer and use it in GitHub Desktop.
cross-browser-text-selection.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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