Automatically Expanding Textarea Elements with Yahoo! UI

An HTML textarea element does not expand automatically to accommodate the contained text. With JavaScript we can make the textarea expand as we add more text or shrink as we remove text. This example looks at the problems with the usual solution and a new solution that works when proportional fonts are used.

The usual method

A textarea element must have rows and cols attributes. These specify the number of monospace characters that can fit down and across the textarea. The method for automatically expanding a textarea that I have seen in other tutorials depends on the rows and cols attributes. The number of characters in the textarea divided by the number of columns equals the number of needed rows. This is fine for monospaced fonts.

When proportionally spaced fonts, with their very skinny 'i' and 'l' characters, are used inside a textarea the method just described falls apart. Since each 'i' doesn't use up it's alloted width in the calculation more rows are calculated than are needed. This example shows the problem in an exaggerated way. Try changing most of the characters to a wide character like 'M'. The excess whitespace problem is not present. (Note, I didn't make much effort in getting this example working nicely with scrollbars etc. This method was a dead end path for me.)

A new method

Instead of calculating the number of rows needed I took a totally different approach. Into the document I inserted a hidden div with the same width and font as the textarea. I did this for each text area. I cloned the contents of the textarea into this hidden div. This div naturally resizes to the correct height needed. I used YAHOO.util.Dom.getRegion() to calculate the height of the div. I then assigned this height (plus one and a half extra rows worth of height) to the textarea. Using setTimeout() I repeat this process every 300 ms or so. The extra one and a half rows makes the user experience nicer when adding to the bottom of the textarea. If they add enough that another row is needed then it is already there for them. They don't have to wait for the current timeout to expire before the textarea is expanded large enough to contain the contents.

New Example

This example degrades acceptably for people without JavaScript. They will see textareas with fixed height and scroll bars if necessary.

Note: This is definitely a hack and it works with reasonable input. Words wider than the textarea will likely cause undesireable results. This is not likely to be a big problem in real applications.

Comments

Have something to write? Comment on this article.

Nicolae Namolovan July 15, 2006

Good ideea ! Last month I used "the usual method" to do that ;) One question, why to use setTimeout when text changes only on user key press ? I think using onkeyup instead of setTimeout will be more rational. Thank for great ideea !

Peter Michaux July 16, 2006

Nicolae,

Thanks for the idea. I did think about using key events. I also thought of some reasons this might cause trouble:

  • There is copy and paste with right mouse clicks to worry about.
  • Sometime I might automatically add some content to the textarea with JavaScript.
  • I don't think it is necessary to update the textarea size every keystroke. That is a lot of updating.

From a maintenance point of view, I didn't want to have to revisit this script as I thought of new events for which I'd have to watch and then trigger an update. It just started to get too complicated in my head. Using setTimeout() is like a catch-all way of doing this with acceptable results for my application.

Alex October 31, 2006

Hey, I tried out this script, and noticed one text property it doesn't take into consideration... line-height. Just needs one more line:

s.lineHeight = getStyle(this.textarea, 'lineHeight');

Other than that, nice work :)

Simon Bohlin November 7, 2008

Your new method with divs is nice, at least some comment should (since you're the only reasonable google result for "textarea rows calculate" ) include a non-YUI solution, like mine?

Instead I did my own solution:

function resize_HTMLTextArea(ta) {
  var t=ta.value.replace(/\r\n/g, '\n').split('\n'), sum=0;
  for (var i=0;i<t.length;i++) if (t[i].length > ta.cols) sum++;
  ta.rows=sum + t.length + 1;
}

Making it more low-tech, I just call this at BODY.onload and textarea onchange.

Still, a drop-in version could be cool, to make this an easy addon only for those browsers still lacking such functionality built in.

mvh
/Simon

vol7ron July 24, 2009

Peter, setTimeOut() is unnecessary. Why not call the function on/after the keypress event? That way you can free up resources when people aren't typing in the box.

Peter Michaux July 24, 2009

vol7ron,

Keypress events are not the only way to enter content into a textarea. It is possible to right click and choose "paste" from the context-sensitive menu or choose "paste" from the browser's "edit" menu. A user can also drag selected text to a new location in the textarea's content. The only way to catch all of these events is using a setTimeout.

vol7ron August 17, 2009

I just read your other post. If they're copy and pasting then what does it matter if they see scrollbars? But still, why not start/stop the timer on focus/blur?

As said before, this is still a nice example.

Have something to write? Comment on this article.