Skip to content

Instantly share code, notes, and snippets.

@toniengelhardt
Created October 9, 2022 10:53
Show Gist options
  • Save toniengelhardt/de10e03c513f1546e6fcf1ba0188aed0 to your computer and use it in GitHub Desktop.
Save toniengelhardt/de10e03c513f1546e6fcf1ba0188aed0 to your computer and use it in GitHub Desktop.
/**
* Get absolute xy position of caret in px.
* Time to execute: ~2.5ms on a 2018 MacBook Pro.
* Inpired by: https://medium.com/@jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
* @param {object} element - the textarea element to obtain coordinates for
*/
export function getCaretXY(element: HTMLTextAreaElement): { x: number, top: number, bottom: number } {
const { top: textElementTop, left: textElementLeft } = element.getBoundingClientRect()
const selectionPoint = element.selectionEnd
// Create a dummy element, copy style and populate with content up to the caret.
const dummy = document.createElement('div')
const computedStyle = getComputedStyle(element)
for (const prop of computedStyle) {
dummy.style[prop] = computedStyle[prop]
}
dummy.textContent = element.value.substring(0, selectionPoint)
dummy.style.position = 'relative' // this is important to get the relative position of the div.
dummy.style.height = 'auto'
// Add a marker element to obtain relative caret position.
const span = document.createElement('span')
span.textContent = ''
dummy.appendChild(span)
document.body.appendChild(dummy)
const {
offsetLeft: caretElementOffLeft,
offsetTop: caretElementOffTop,
offsetHeight: caretElementHeight,
} = span
// Remove the dummy element.
document.body.removeChild(dummy)
return {
x: textElementLeft + caretElementOffLeft, // Width is 0px.
top: textElementTop + caretElementOffTop,
bottom: textElementTop + caretElementOffTop + caretElementHeight,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment