you can use document.caretPositionFromPoint
for this, if you know or can approximate the line height of the text in the container. Here is an example fiddle: https://jsfiddle.net/xtcadpo0/
The function basically allows us to grab the offset position of text for any element at the given coordinates, and then we use simple string slicing on the textContent
property of the element. We use this to grab the position of the point just before the text we need, and get the offset.
This solution is quite friendly to DOM performance as well, since we don't insert new elements or mutate the ones already existing.
Hope that helps.
Edit: just saw that you want to wrap the last line in a span, which you can do like this: https://jsfiddle.net/xtcadpo0/1/
It splits the text node and replaces the replacement with a wrapped element.
Here is the code:
function fn(el) {
var range;
var textNode;
var offset;
var e = {};
var height = el.clientHeight;
var lh = parseFloat(getComputedStyle(el).lineHeight, 10) || 18;
var D = height / lh;
var secondLastLineStart = (D - 1.5) * lh;
var pos = el.getBoundingClientRect();
e.x = pos.right;
e.y = secondLastLineStart;
if (document.caretPositionFromPoint) {
range = document.caretPositionFromPoint(e.x, e.y);
textNode = range.offsetNode;
offset = range.offset;
} else if (document.caretRangeFromPoint) {
range = document.caretRangeFromPoint(e.x, e.y);
textNode = range.startContainer;
offset = range.startOffset;
}
var repl = textNode.splitText(offset);
var newEl = document.createElement('span');
textNode.parentNode.replaceChild(newEl, repl);
newEl.appendChild(repl);
}
fn(document.getElementById('x'));