Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131//https://github.com/component/textarea-caret-position/blob/master/index.jslet properties = [ "direction", // RTL support "boxSizing", "width", // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does "height", "overflowX", "overflowY", // copy the scrollbar for IE
"borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth", "borderStyle",
"paddingTop", "paddingRight", "paddingBottom", "paddingLeft",
// https://developer.mozilla.org/en-US/docs/Web/CSS/font "fontStyle", "fontVariant", "fontWeight", "fontStretch", "fontSize", "fontSizeAdjust", "lineHeight", "fontFamily",
"textAlign", "textTransform", "textIndent", "textDecoration", // might not make a difference, but better be safe
"letterSpacing", "wordSpacing",
"tabSize", "MozTabSize",];
var isBrowser = typeof window !== "undefined";//@ts-ignorevar isFirefox = isBrowser && window.mozInnerScreenX != null;
export function getCoordinatesInTextarea( element: HTMLTextAreaElement, position: number,) { if (!isBrowser) { throw new Error( "textarea-caret-position#getCaretCoordinates should only be called in a browser", ); }
// The mirror div will replicate the textarea's style var div = document.createElement("div"); div.id = "input-textarea-caret-position-mirror-div"; document.body.appendChild(div);
var style = div.style; var computed = window.getComputedStyle(element); var isInput = element.nodeName === "INPUT";
// Default textarea styles style.whiteSpace = "pre-wrap"; if (!isInput) style.wordWrap = "break-word"; // only for textarea-s
// Position off-screen style.position = "absolute"; // required to return coordinates properly style.visibility = "hidden"; // not 'display: none' because we want rendering
// Transfer the element's properties to the div properties.forEach(function (prop) { //@ts-ignore style[prop] = computed[prop]; });
if (isFirefox) { // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 if (element.scrollHeight > parseInt(computed.height)) style.overflowY = "scroll"; } else { style.overflow = "hidden"; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' }
div.textContent = element.value.substring(0, position); // The second special handling for input type="text" vs textarea: // spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037 if (isInput) div.textContent = div.textContent.replace(/\s/g, "\u00a0");
var span = document.createElement("span"); // Wrapping must be replicated *exactly*, including when a long word gets // onto the next line, with whitespace at the end of the line before (#7). // The *only* reliable way to do that is to copy the *entire* rest of the // textarea's content into the <span> created at the caret position. // For inputs, just '.' would be enough, but no need to bother. span.textContent = element.value.substring(position) || "."; // || because a completely empty faux span doesn't render at all div.appendChild(span);
var coordinates = { top: span.offsetTop + parseInt(computed["borderTopWidth"]), left: span.offsetLeft + parseInt(computed["borderLeftWidth"]), height: parseInt(computed["lineHeight"]), };
document.body.removeChild(div); return coordinates;}
export function getPosAtCoordinates(x: number, y: number) { let textNode; let offset;
if (document.caretPositionFromPoint) { let caretPosition = document.caretPositionFromPoint(x, y); textNode = caretPosition?.offsetNode; offset = caretPosition?.offset; } else if (document.caretRangeFromPoint) { // Use WebKit-proprietary fallback method let range = document.caretRangeFromPoint(x, y); textNode = range?.startContainer; offset = range?.startOffset; } return { textNode, offset, };}