Question

How can I read the current value of textarea.rows and cols?

I expected the .rows and .cols property of a textarea element in javascript to tell me the current values, but they don't, they seem to tell me just the initial markup attribute values, even after the textarea is resized.

function howBigAmI(){
  let el= document.getElementById("textarea")
  
    document.getElementById("output").innerHTML=
    `${el.rows} rows by ${el.cols} cols, text length=${el.value.length}`  
    
}
howBigAmI()
<p>
How can I read the current textarea rows and cols?
</p>
<textarea oninput="javascript:howBigAmI()" id="textarea">
how big am I?
</textarea>
<div id="output">size</div>

How can I get the current values?

(lineHeight property can't be relied on unless I explicitly set it, so that's not a solution.)

(I want to calculate how many rows are needed to show all of the textarea's current content, if the width remains unchanged. But I don't want to have to set either of the properties to be able to do the calculation.)

 2  77  2
1 Jan 1970

Solution

 1

Probably, the best option would be to calculate scrollHeight / offsetHeight in the same <textarea>, but with row="1"

const getRows = textarea => {
  const computedStyle = getComputedStyle(textarea);
  const { offsetWidth: realOffsetWidth, scrollWidth: realScrollWidth } =
    getSize(textarea);
  const realScrollbarWidth = realOffsetWidth - realScrollWidth;

  const tempElement = document.createElement('textarea');

  tempElement.rows = 1;
  tempElement.style.font = computedStyle.font;
  tempElement.style.border = computedStyle.border;
  tempElement.style.padding = computedStyle.padding;
  tempElement.style.lineHeight = computedStyle.lineHeight;
  tempElement.style.overflowY = 'scroll';
  tempElement.style.position = 'absolute';
  tempElement.style.visibility = 'hidden';

  document.body.appendChild(tempElement);

  const { offsetWidth, scrollWidth } = getSize(tempElement);
  const scrollbarWidth = offsetWidth - scrollWidth;
  tempElement.style.width = realScrollbarWidth
    ? realOffsetWidth + 'px'
    : realOffsetWidth + scrollbarWidth + 'px';

  tempElement.textContent = textarea.value;

  const { offsetHeight, scrollHeight } = getSize(tempElement);

  document.body.removeChild(tempElement);

  return Math.floor(scrollHeight / offsetHeight);
};

const getSize = el => {
  const computedStyle = getComputedStyle(el);
  const paddingTop = parseFloat(computedStyle.paddingTop);
  const paddingBottom = parseFloat(computedStyle.paddingBottom);
  const paddingLeft = parseFloat(computedStyle.paddingLeft);
  const paddingRight = parseFloat(computedStyle.paddingRight);
  const borderTop = parseFloat(computedStyle.borderTopWidth);
  const borderBottom = parseFloat(computedStyle.borderBottomWidth);
  const borderLeft = parseFloat(computedStyle.borderLeftWidth);
  const borderRight = parseFloat(computedStyle.borderRightWidth);

  const offsetHeight =
    el.offsetHeight - paddingTop - paddingBottom - borderTop - borderBottom;
  const scrollHeight = el.scrollHeight - paddingTop - paddingBottom;
  const offsetWidth =
    el.offsetWidth - paddingLeft - paddingRight - borderLeft - borderRight;
  const scrollWidth = el.scrollWidth - paddingLeft - paddingRight;

  return { offsetHeight, scrollHeight, offsetWidth, scrollWidth };
};

output.textContent = getRows(textarea);

textarea.addEventListener('input', function () {
  output.textContent = getRows(this);
});

const resizeObserver = new ResizeObserver(() => {
  output.textContent = getRows(textarea);
});
resizeObserver.observe(textarea);
<p>
How can I read the current textarea rows and cols?
</p>
<textarea id="textarea">how big
am I?</textarea>
<div id="output"></div>

2024-07-22
imhvost

Solution

 0

Can the "needed height" be in pixels, or you absolutely need it in "row units"?

If pixels are an option, use el.scrollHeight, like this:

function howBigAmI(){
    let el= document.getElementById("textarea")
    document.getElementById("output").innerHTML = 'textarea should be ' + el.scrollHeight + ' pixels high to fit all the content';
}
2024-07-22
Congregator