On April 7th, Google launched a
new version of Gmail for mobile for iPhone and Android-powered devices. We shared the
behind-the-scenes story through this blogand decided to share more of
what we've learned in a brief series of follow-up blog posts. This week I'll talk about
autogrowing textareas for entering large amounts of text.
When composing a long message in a web app, regardless of whether
it's on a desktop or a mobile device, you really want to see as much of your draft as possible
and make use of all the available screen space.
One of
my biggest gripes are fixed-size textareas that restrict me to only a couple lines of visible
text when my screen is actually many times larger than the size of the textarea.
In today's blog post, I'll share a JavaScript solution for
textareas that automatically grow (vertically) to the size of the content. They make composing
long messages much easier and, for all those iPhone users out there, takes away the need to
scroll with the dreaded magnifying glass! We're working on getting this into Gmail for mobile
but here it is now as a teaser of things to come.
Measuring the height of the content
The first
step is to detect when the content has changed. Some solutions on the net recommend using a
timer (see our previous
post to find out more about timers) to check if content has changed. However, that
approach is not ideal on a mobile device, where both battery life and processor power are
limited.
Instead, we will listen for key-up events from the browser.
This guarantees that we only measure the textarea when the content has actually changed.
The second step is to actually measure the height of the content. There are solutions on
the net that recommend keeping a copy of the content in a div and measuring the div to get the
height; however, due to memory and processor limitations on a mobile device, those solutions
don't scale well when the content gets large (and it's also hard to replicate textarea line
wrapping behavior exactly in a div).
Therefore we will make use of the
scrollHeight and clientHeight properties. For our purposes, scrollHeight is the height of all
the content while clientHeight is the height of the content that's visible in the textarea
(for more precise definitions, see scrollHeight and
clientHeight)
function grow() { var textarea =
document.getElementById('growingTextarea'); var newHeight =
textarea.scrollHeight; var currentHeight = textarea.clientHeight; … }
One limitation of using scrollHeight
and clientHeight is that we aren't able to shrink the textarea when content is deleted. When
all the content of a textarea is visible, the scrollHeight is equal to the clientHeight.
Therefore we aren't able to detect that our textarea is actually larger than the minimum size
required to fit all the content (please do leave a comment if you think of a solution that
doesn't require re-rendering the page).
Growing the textarea
To grow the text area, we
modify the height CSS property:
Notice how we only change the height if
newHeight > currentHeight. Depending on the browser, changing the height (even if it's to
the same value) will cause the page to re-render. On a mobile device, we want to try our best
to minimize the number of operations.
Also, we grow the textarea by
five lines every time we grow. From a UI perspective, this reduces the amount of jitter when
composing a message but, from a performance perspective, this reduces the number of times we
need to re-render the page.
(Quick note for developers implementing
this for a browser with scrollbars: you might want to modify the CSS overflow property to
preventing the scrollbar from appearing and disappearing as you grow your textarea)
The complete
solution
Growing textareas are easy to implement and they
make composing long messages infinitely more usable. So go out there add it to all your web
apps!
<script> // Value of the
line-height CSS property for the textarea. var TEXTAREA_LINE_HEIGHT = 13;
function grow() { var textarea =
document.getElementById('growingTextarea'); var newHeight =
textarea.scrollHeight; var currentHeight = textarea.clientHeight;