This particular issue presents a challenge because attempting to call the getBoundingClientRect()
method on a range within an <input>
or <textarea>
element will result in all browsers returning 0s for the rect values. Further details can be found here: How to get the bounding rect of selected text inside an <input>?
One workaround is to "clone" the node as a <div>
, replicate the style and content of the <textarea>
, and then identify the rects using the <div>
. By calculating the height of all text and dividing it by the height of one character (the line-height) in the selection, you can obtain the required information.
I encountered this scenario while working on a project and found this method to be the most dependable approach for obtaining geometric data about text within <input>
and <textarea>
elements.
const clone = document.createElement('div');
const range = document.createRange();
const textarea = document.querySelector('textarea');
let rect = textarea.getBoundingClientRect();
let lineHeight;
let totalHeight;
// Clone the textarea and insert it into the DOM
clone.style.cssText = window.getComputedStyle(textarea).cssText;
clone.style.left = rect.left + 'px';
clone.style.position = 'absolute';
clone.style.top = rect.top + 'px';
clone.textContent = textarea.value;
document.body.appendChild(clone);
// Determine the number of visible rows
range.setStart(clone.firstChild, 0);
range.setEnd(clone.firstChild, 1);
rect = range.getBoundingClientRect();
lineHeight = rect.height;
range.setEnd(clone.firstChild, clone.textContent.length);
rect = range.getBoundingClientRect();
totalHeight = rect.height;
console.log(totalHeight / lineHeight);
document.body.removeChild(clone);
<textarea rows="4">Long text here foo bar lorem ipsum Long text here foo bar lorem ipsum Long text here foo bar</textarea>