Skip to content

Instantly share code, notes, and snippets.

@nothingismagick
Last active April 16, 2024 10:09
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save nothingismagick/642861242050c1d5f3f1cfa7bcd2b3fd to your computer and use it in GitHub Desktop.
Save nothingismagick/642861242050c1d5f3f1cfa7bcd2b3fd to your computer and use it in GitHub Desktop.
Small script to detect caret pixel position in contenteditable div
/**
* 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) }
}
}
@oscxc
Copy link

oscxc commented May 11, 2023

Excellent! Thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment