Measuring the y-position of selected text

Measuring the position of selected text is pretty tricky. Its easy to calculate the position of the container, but this is often unprecise, because paragraphs can be very long.

The position can for example be used to scroll the selection into view.

Here is how I solved it. It only works in firefox, but it should be pretty easy to convert.

 

BASTARD.getNodeOffsetTop = function(elm) {
if (!elm) return;
var y=0;
while (elm!=document.body) {
y+=elm.offsetTop;       
elm = elm.offsetParent;
}
return y;
}

// Calculates the y-position of the selected text within the document
// pretty tricky, partly because range.insertNode misbehaves
BASTARD.getOffsetTopOfSelection = function(win) {
var sel = win.getSelection();
var r = sel.getRangeAt(0);
var startContainer = r.startContainer;
var endContainer = r.endContainer;
var startOffset = r.startOffset;
var endOffset = r.endOffset;
var isSameContainer = (startContainer == endContainer);
var elm = r.startContainer;
var offset = r.startOffset;
sel.removeAllRanges();
var rest = elm.splitText(offset);
var dummyNode = win.document.createElement("span");
//dummyNode.appendChild(win.document.createTextNode("Y"));
rest.parentNode.insertBefore(dummyNode,rest);
var offsetTop = BASTARD.getNodeOffsetTop(dummyNode);
var r = document.createRange();
r.setStart(rest, 0);
if (isSameContainer) {
r.setEnd(rest, endOffset - startOffset);
}
else {
r.setEnd(endContainer, endOffset);
}
sel.addRange(r);
return offsetTop;
}


// Scroll document down, but only if needed, and only as much as needed
BASTARD.scrollSelectionIntoView = function(win) {
var offsetTop = BASTARD.getOffsetTopOfSelection(win)
var dy = win.innerHeight + win.pageYOffset - offsetTop - 40;
if (dy < 0) {
win.scrollBy(0, -dy);
}
}

Back to top