-
-
Save nothingismagick/642861242050c1d5f3f1cfa7bcd2b3fd to your computer and use it in GitHub Desktop.
/** | |
* Get the caret position in all cases | |
* | |
* @returns {object} left, top distance in pixels | |
*/ | |
getCaretTopPoint () { | |
const sel = document.getSelection() | |
const r = sel.getRangeAt(0) | |
let rect | |
let r2 | |
// supposed to be textNode in most cases | |
// but div[contenteditable] when empty | |
const node = r.startContainer | |
const offset = r.startOffset | |
if (offset > 0) { | |
// new range, don't influence DOM state | |
r2 = document.createRange() | |
r2.setStart(node, (offset - 1)) | |
r2.setEnd(node, offset) | |
// https://developer.mozilla.org/en-US/docs/Web/API/range.getBoundingClientRect | |
// IE9, Safari?(but look good in Safari 8) | |
rect = r2.getBoundingClientRect() | |
return { left: rect.right, top: rect.top } | |
} else if (offset < node.length) { | |
r2 = document.createRange() | |
// similar but select next on letter | |
r2.setStart(node, offset) | |
r2.setEnd(node, (offset + 1)) | |
rect = r2.getBoundingClientRect() | |
return { left: rect.left, top: rect.top } | |
} else { // textNode has length | |
// https://developer.mozilla.org/en-US/docs/Web/API/Element.getBoundingClientRect | |
rect = node.getBoundingClientRect() | |
const styles = getComputedStyle(node) | |
const lineHeight = parseInt(styles.lineHeight) | |
const fontSize = parseInt(styles.fontSize) | |
// roughly half the whitespace... but not exactly | |
const delta = (lineHeight - fontSize) / 2 | |
return { left: rect.left, top: (rect.top + delta) } | |
} | |
} |
To handle long selections (e.g. contenteditable content higher than the viewport and selection range spanning over longer distances than the height of the viewport) you have to figure out the caret position within the selection (it's either start or end of the selection).
In practice, you can do this by using window.getSelection()
and focusNode
and focusOffset
.
Then you can build a range out of those and use Range.getBoundingClientRect()
to figure out the pixel offsets within the viewport, but beware bug https://bugs.chromium.org/p/chromium/issues/detail?id=1398728
And an interesting question is what is the true pixel location of the caret if the caret position is between two consequtive <table>
elements where one is very small and another is higher than the viewport? The above bug is basically about the same issue but it will show up more often than simply between consequtive tables.
Excellent! Thank you very much!
big, thanks it works, love you :)