Build an Elastic Textarea with Ext JS

Aug 28 2009 by Nuno Franco da Costa | 28 Comments

Build an Elastic Textarea with Ext JS

Since it was first featured on Facebook, elastic textareas – <textarea> elements that automatically expand or shrink depending on how much text the user inputs – has become one of the coolest functional UI effects on the web. In this article, I will guide you through the re-creation of this astonishing effect using Ext JS, and I bet you that you will be surprised to see how easy it is to do it.

Live Demo

Check out the example of what we will be creating by clicking on the following link.

What you will need

For this tutorial, we will use the Ext JS JavaScript framework – my favorite framework for cross-browser web application development. I will explain the mechanics step by step so that if you prefer, you can implement the same programming logic using another JavaScript framework/library or with just JavaScript.

Only the Ext Core will be needed in this example.

What is Ext JS?

Ext JS describes itself as being a "cross-browser JavaScript library for building rich internet applications". Although it’s true, this description is too succinct to describe this excellent JavaScript framework’s power and capabilities.

It’s the perfect tool for creating Rich Internet Applications (RIA): it has a commanding effects library and an awesome GUI toolkit, providing you with tons of components that mimic desktop applications’ fluidity and complexity on the web.

Ext JS is available in two flavors: Ext Core and Ext JS.

Ext Core is the "lite version" of Ext JS, offering the same kind of functionalities you can find in other popular JS frameworks (like jQuery or MooTools). It’s licensed under the very permissive MIT license.  Some functions that are available in Ext JS are not available in Ext Core though – but in our instance, it’s quite alright.

Ext JS is packed with a lot of extra features and lots of GUI gadgets. You can get it under one of the two licenses: under a GPL or a commercial license that waives the GPL restrictions. It’s extremely well documented with tons of demos and an easy to follow manual. It also has great community support, although they can be a bit harsh to beginners who don’t first search if their question has been answered already before asking questions. If you’re interested in learning about Ext JS more fully, check out this great tutorial that is available here.

The Issue

Unfortunately, there is no textarea.contentHeight or similar method, so forget about a direct way to get the text height.

"So how do I make the textarea grow and shrink?"

A possible approach to find a textarea‘s height is to get the font size and textarea width and count the number of characters typed. But then when you press the Enter key your calculations are messed up.

"Ok, that’s not a problem… I can put the Enter key into the equation" you might think, but then you have to think about CSS formatting (i.e. padding, line height, or font size).

You see how this can become a nightmare in no time?

Don’t despair: there is an easy way using Ext JS!

The Trick

To make the textarea grow or shrink, you need to get its content’s height and take into account the CSS styles that affect text formatting like font-size, line-height, etc.

The trick to get text height is to mimic the textarea contents in a hidden div.

To get a decent effect, we will also limit the textarea minimum and maximum sizes, there is no beauty in a 0-px height textarea.

Alright, it’s time to get our hands dirty.

Step 1: Laying down the foundations

First step is to create a function and set up some control variables; we shall name this function elasticTextArea.

We also declare two extra functions that allow us to get and set all CSS styles at once.

function elasticTextArea (elementId){
/*
 * This are two helper functions, they are declared here for convenience
 * so we can get and set all styles at once and because they are not 
 * available in ext-core only in Ext JS
 */
 //available in Ext JS (not ext-core) as element.getStyles
 function getStyles(el, arguments){
  var ret = {};
  total = arguments.length;
  for (var n=0; n< len;) {
   el.setStyle(styles[i++], styles[i++]);
    }
     } else if (Ext.isObject(styles)) {
     el.setStyle(styles);
   }
  }
 }
 //minimum and maximum textarea size
 var minHeight = 10;
 var maxHeight = 300;
 //increment value when resizing the textarea
 var growBy = 20 ;
 var el = Ext.get(elementId);

Step 2: Get the textarea CSS styles

To get an accurate measurement, we need to get the textarea CSS styles that affect text formatting, and they are:

  • padding
  • font-size (and other font styles)
  • font-family
  • line-height
  • width
//get textarea width
 var width = el.getWidth();
//current textarea styles
 var styles = el.getStyles('padding-top', 'padding-bottom', 'padding-left',
              'padding-right', 'line-height', 'font-size',
              'font-family', 'font-weight', 'font-style');
//store textarea width into styles object to later apply them to the div
styles.width = width +'px';

//hide the textarea scrool to avoid flickering
el.setStyle('overflow', 'hidden');

Step 3: Create the hidden div

And here’s where the magic begins. We start by creating a hidden container for the textarea contents.

We set its position to absolute — this way, the div is removed from the layout’s flow and positioned outside the visible area of the browser.

Note: setting its visibility CSS attribute to hidden or its display to none causes some browsers not to recalculate its height, that’s why we opted for this method.

We also instruct the textarea to recalculate its height by re-running this function on every key stoke; another method is to run it at a specific interval, but I find it to be more resource-intensive and less elegant that way.

 //create the hidden div only if does not exists
 if(!this.div){
  //create the hidden div outside the viewport area
  this.div = Ext.DomHelper.append(Ext.getBody() || document.body, {
   'id':elementId + '-preview-div',
   'tag' : 'div',
   'style' : 'position: absolute; top: -100000px; left: -100000px;'
  }, true);
 //apply the textarea styles to the hidden div
 Ext.DomHelper.applyStyles(this.div, styles);
 //recalculate the div height on each key stroke
 el.on('keyup', function(){
  elasticTextArea(elementId);
 }, this);
}

Step 4: Copy textarea contents to the hidden div and get its height

To ensure a correct measurement, we replace some special characters with their HTML character entities and also append a space (' ') string to the new line to force its representation.

/* clean up textarea contents, so that no special chars are processed
 * replace \n with so that the enter key can trigger a height increase
 * but first remove all previous entries, so that the height measurement
 * can be as accurate as possible
 */
 this.div.update(
  el.dom.value.replace(//, '')
  .replace(/<|>/g, ' ')
  .replace(/&/g,"&")
  .replace(/\n/g, '')
 );
 //finally get the div height
 var textHeight = this.div.getHeight();

Step 5: Resize the textarea

In the last step, we give the textarea its new height.

  //enforce textarea maximum and minimum size
  if ( (textHeight > maxHeight ) && (maxHeight > 0) ){
   textHeight = maxHeight;
   el.setStyle('overflow', 'auto');
  }
  if ( (textHeight < minHeight ) && (minHeight > 0) ){
   textHeight = minHeight ;
  }
 //resize the textarea
 el.setHeight(textHeight + growBy , true);
}

Try it out

Wasn’t that simple? Save the JavaScript code we wrote as elasticTextarea.js or download the source, include Ext Core and the JavaScript library, and have fun.

To include the JS libraries:

<script type="text/JavaScript" src="ext-core/ext-core.js"></script>
<script type="text/JavaScript" src="elastic.js"></script>

The HTML for the text area:

<textarea id="ta"></textarea>

To call the function (‘ta’ being the ID of the textarea):

<script type="text/JavaScript">
 elasticTextArea("ta");
</script>

Conclusion

The purpose of this article is to introduce you to Ext JS. If you know your JavaScript well, you might find that this code will only work for one textarea. If you’d like a more robust and flexible solution, get the Ext JS plugin that can be found over at my site.

Stay tuned for more Ext JS tutorials here on Six Revisions, written by me. Future tutorials will cover Ext JS plugins, and talk about more cool and easy-to-create UI effects using Ext JS RIA framework.

Related Content

About the Author

Nuno Franco da Costa is a web developer and sys admin. By day, he works at a design agency coordinating the development and sys admin teams where he developed a PHP MVC framework and a WEB 2 CMS. He loves to code and has a "getting things done" attitude. You can find over at his online presence www.francodacosta.com.

28 Comments

Kawsar Ali

August 27th, 2009

This is really useful. Saves a lot of space. A wordpress plugin would be cool.

Jason

August 27th, 2009

Awesome tutorial! I noticed in the demo (with Firefox 3.5.2 Vista) that once you hit a certain length, a scroll bar appears – which is great. However, there seems to be an undesirable effect where after each character you type (once the scrollbar appears) the viewable area scrolls back to the top, so you get this constant vertical flicker as you type as the window keeps going back to the top, then down as you hit a character, then back up, then down again on next character etc. Dont know if thats a bug or just a necessary evil of the textarea becoming “too” tall.

Edem

August 27th, 2009

I tried the demo, and it has a nasty bug: if you type so much that the size won’t grow anymore but you get a scrollbar, then after any typed letter the view jumps back to the top (though the cursor stays at the end of the box…)

Tested on: Firefox 3.5

Fábio ZC

August 27th, 2009

“I tried the demo, and it has a nasty bug: if you type so much that the size won’t grow anymore but you get a scrollbar, then after any typed letter the view jumps back to the top (though the cursor stays at the end of the box…)

Tested on: Firefox 3.5″

Exactly the same thing!

nuno costa

August 27th, 2009

That’s the good thing about this profession; one must learn to live with his mistakes!

Thank you all for pointing this nasty bug, shame on me!

My first reaction to this was “that’s easy, just scroll the textarea to the end”, and it worked great until I edited some text in the middle!

The solution is to get the cursor position before resizing the textarea and after just set it again.

I’ve updated the demo and source download

And thanks again for pointing this out, I completely missed it because I tend to use the plugin without a fixed size.

Jacob Gube

August 27th, 2009

Thanks for letting us know about the issue – we’re updating the code and demo to address this bug: stay tuned.

laurie

August 27th, 2009

I noticed another bug, which is probably less of an issue, much none-the-less..

Having not yet looked at the code and just played with the effect ;-) if you hold down a key and keep it pressed:

Firefox 3.5 : The text goes beyond the size of the box

IE 8 : The box stays the same size but follows the caret

Chrome : The box stays the same size but follows the caret until the key is depressed, at which point the box “grows” to the correct size.

HTH

Roberto

August 27th, 2009

Very good. I’ll soon give it a try. Thanks for sharing.

Leon

August 28th, 2009

great stuff! Does it play nicely with jquery?

Wiz

August 28th, 2009

Yeah… same thing here – Works great at first, its just that bug that happens when the scrollbar appears. Other than that, awesome!

Wiz

August 28th, 2009

I actually just noticed laurie’s comment and wanted to see what would happen if i was just typing really fast.

Conclusion: the same thing happened. The text kept writing but the box didn’t resize… so the text was coming up 1 line below where the box ended. When i got to the end of that line, the box resized so I could see it… but then I was already on the next line typing more. The bug repeats this error each time.

Jonathan Bennett

August 28th, 2009

That’s a pretty sweet effect. I don’t see it used often. Thanks for the bug updates.

Misha Peric

August 28th, 2009

I think ExtJS textarea already supports this feature out of the box.

grow : Boolean
true if this field should automatically grow and shrink to its content (defaults to false)

nuno costa

August 28th, 2009

@laurie

Long words can be a problem for a narrow textarea. This has to do with the way each browser handles long words within the textarea, if the word is too long to be displayed in some will spit the word and continue in the next line while others wont’t.

I’ve been using this in a lot in contact forms with great feed back, users can write all they want and see what they have typed and the clients just love the effect!

@Misha Peric

It’s not quite the same thing, you are talking about a feature of an extjs widget not the textarea element. Actually this was debated in the Ext Forums when I first released this, there are some performance and code size advantages by not having to load all of Ext.Form classes and it’s dependences just for this and those widgets are not included in the Ext Core release

Yash

August 31st, 2009

Great info! Really cool. Thanks.

dave

August 31st, 2009

this textarea sux .. try write more than few lines of text

Jaan

August 31st, 2009

If u use the space bar it doesn’t expand:(

nurettin

September 7th, 2009

Great work thanks for all script :)

Tim

September 16th, 2009

Love it! but is there a way to use it for more then one textarea per page? I’d like to use in a form with several textareas.

I tried different ID with seperate calls and other combinations.

Thanks

joet

January 15th, 2010

80k for the core? that’s way too big for mobile users.

bea

March 8th, 2010

Does not work in IE7 or FF3. I see side scrollbars if I type a paragraph. In FF, it keeps going to the top.

dan

May 20th, 2010

Doesn’t work for multiples textarea!!

THORN

November 16th, 2010

Thank you man I like it (:

Tor Olsson

December 1st, 2010

First of, great script!
I hate oversized textareas! ;)

I had a little problem though on a page where the textarea resides within a hidden div (display: none).

When your script creates the div it gets 0 width. This makes the textarea, when visible, grow on every typed whitespace character.

I made a minor change in the script to fix this. I simply update the div’s width if it has changed.

if (!this.div) {
..
} else if (this.div.getWidth() != width) {
this.div.setWidth(width);
}

Tor Olsson

December 1st, 2010

Made another improvement, support of multiple textareas.

I just store the created div in an array instead of in a single object (this.div).

I added:

if (!this.divArray) {
this.divArray = new Array();
}

before “if (!this.div) {” in the original script and replaced every occurrence of “this.div” with “this.divArray[elementId]“.

Milan Zdimal

August 4th, 2011

Just an optimization thing… Setting the height of the DOM element is expensive and the height change is very delayed when you are typing fast. I found it’s better to store the last height and only set a new height if the last height and the new height are not equal.

i.e.
if(this.lastHeight != textHeight)
el.setHeight(textHeight + growBy , true);

this.lastHeight = textHeight;

Milan Zdimal

August 4th, 2011

Also, I just noticed that the ExtJS textarea object has this built in. Just set the grow property to true, http://docs.sencha.com/ext-js/4-0/#/api/Ext.form.field.TextArea

James Payne

September 2nd, 2012

Can I use this code in my project which I will use for business purpose? By doing so, will there be any copyright violation? In other words, is it LEGAL?

Leave a Comment

Subscribe to the comments on this article.