Расширение ответа Жюльена выше.Это справляется с несколькими строками.Нужна небольшая настройка, но, похоже, работает.Он находит количество строк, получая высоту начального и конечного выделения, а также высоту выделения из одной буквы, деления двух и округления.Вероятно, существуют ситуации, когда это не сработает, но для большинства целей ...
function getLineCount(node, range) {
if ((node) && (range)) {
range.setStart(node, 0);
range.setEnd(node, 1);
var r = range.getBoundingClientRect();
var h1 = r.bottom - r.top;
range.setEnd(node, node.length);
r = range.getBoundingClientRect();
return Math.round((r.bottom - r.top) / h1);
}
};
Вот измененная версия приведенного выше кода с использованием процедуры подсчета строк, описанной выше.Он также немного лучше справляется с выделениями внутри узла, но справа от самого текста.Ничто из этого не оптимизировано, но мы здесь во времени пользователя, так что миллисекунды, вероятно, не слишком важны.
function getSelectionNodeInfo(x, y) {
var startRange = document.createRange();
window.getSelection().removeAllRanges();
window.getSelection().addRange(startRange);
// Implementation note: range.setStart offset is
// counted in number of child elements if any or
// in characters if there is no childs. Since we
// want to compute in number of chars, we need to
// get the node which has no child.
var elem = document.elementFromPoint(x, y);
console.log("ElementFromPoint: " + $(elem).attr('class'));
var startNode = (elem.childNodes.length>0?elem.childNodes[0]:elem);
var lines = getLineCount(startNode, startRange);
console.log("Lines: " + lines);
var startCharIndexCharacter = 0;
startRange.setStart(startNode, 0);
startRange.setEnd(startNode, 1);
var letterCount = startNode.length;
var rangeRect = startRange.getBoundingClientRect();
var rangeWidth = 0
if (lines>1) {
while ((rangeRect.bottom < y) && (startCharIndexCharacter < (letterCount-1))) {
startCharIndexCharacter++;
startRange.setStart(startNode, startCharIndexCharacter);
startRange.setEnd(startNode, startCharIndexCharacter + 1);
rangeRect = startRange.getBoundingClientRect();
rangeWidth = rangeRect.right - rangeRect.left
}
}
while (rangeRect.left < (x-(rangeWidth/2)) && (startCharIndexCharacter < (letterCount))) {
startCharIndexCharacter++;
startRange.setStart(startNode, startCharIndexCharacter);
startRange.setEnd(startNode, startCharIndexCharacter + ((startCharIndexCharacter<letterCount) ? 1 : 0));
rangeRect = startRange.getBoundingClientRect();
rangeWidth = rangeRect.right - rangeRect.left
}
return {node:startNode, offsetInsideNode:startCharIndexCharacter};
}