A better way to automatically resize text areas
There are various techniques to make textarea elements automatically resize themselves to accomodate their contents. Facebook, in particular, frequently uses dynamically resizing fields. Among the tricks I have seen proposed or in actual use:
- Counting the number of line breaks in the text and setting the
rowsattribute to this number. This doesn’t work unless you turn off word wrapping in the text field. - Creating a hidden
divelement having roughly the same CSS styles as thetextarea, populating it with the text, then measuring the element’sclientHeight. This doesn’t work consistently because HTML is wrapped text differently fromtextareaelements, even if you sprinkle zero-width space characters across the text; the end result is that eventually, the field height will no longer correctly track the real height of the contents. This is, I believe, the trick used by Facebook. - Detecting whether the text area has scrolled and then increasing the
rowsattribute until it no longer scrolls. I won’t even comment on this one.
None of these techniques are completely satisfactory; all of them have flaws that interfere with the user experience.
After a bit of experimentation, I found a working technique that, it turns out, is so simple it can be summarized as follows: Create a hidden textarea and track its scrollHeight.
Here’s how it works. The scrollHeight attribute tracks the scrollable height of the element’s contents, in pixels. By continually feeding the current text into the hidden text area, we can read the height and resize the visible text area. (Why use a hidden text area and not read scrollHeight from the actual text area? Because scrollHeight changes depending on the height of the element.) While there are few quirks that need to be accounted for, browsers actually implement scrollHeight pretty consistently for text fields.
First, we create a hidden text area. Then we copy whatever style attributes (font family, font size, padding, etc.) from the original field to the dummy. Since an element’s scroll height is only available when it is visible on the page, the hidden element needs to be partially visible; the best way is to use the visibility property:
element.style.visibility = "hidden";
element.style.position = "absolute";
element.style.height = "1px";
This ensures that the element will be rendered, but will not interfere with the rendering of the rest of the page.
To magic a textarea, simply invoke the script like so:
<script>
new DynamicTextAreaResizing("my_text_area");
</script>
Here’s a small demo.
I have tested the code with Firefox 3, Safari 4, Internet Explorer 7 and Opera 9.6.
A few notes on Firefox and Opera, though. Among all current browsers, Firefox is actually the least willing to make this technique work; Opera is better, but not by much. To start, Firefox does not wrap words that are wider than the field itself; Safari and IE both force a break, which is more convenient for the user than dealing with a horizontal scroll bar. (If you think this is rare, consider Flash or YouTube embeds, which often contain long links.)
For this reason, the script does not set the field’s overflow style to hidden, because doing so would prevent the horizontal scroll bar from showing in such cases. And for that reason, the resizing algorithm has to add a (fairly arbitrary) number of pixels to the height to accomodate the possibility of a horizontal scroll bar. Firefox is also considerably slower than the other browsers in rendering @textarea@s, which may be why it will briefly flash a vertical scroll bar just before the field is resized.
- Download the script (BSD license, currently requires Prototype)
- GitHub repository