views:

12341

answers:

18
+22  Q: 

Autosizing Textarea

A: 

The only possible way I can think of is to make a Javascript function executed in an onKeyUp event which will find the number of lines and the longest line in the inputted text, and use those numbers as the cols and rows attributes of the textarea. I'm not sure if that would work well though, and I don't know for sure if it's impossible to do with just CSS or HTML.

yjerem
A: 

I'm not sure I agree that it would look nicer if the textarea automatically resized. That could be pretty distracting, and it would be totally different from the usual UI experience with a textarea. I think you'd be better served by just styling it to look as attractive as possible and avoid the non-standard gimmicks. I'm not suggesting that it's never okay to go outside the usual stuff, but this appears to be an internal, sales-oriented database application. The target audience wants to get their work done, so they probably want efficient and not necessarily new.

@Mike's response... You say it has to be large and readable. If the other text (labels and dropdowns and such) are "regular" size, then I think you have plenty of room to even reduce the font size of the address. I guess it's still a little vague to me exactly what all the features in your layout really are, so I'm probably missing the key nuances. That being said, I think Jeremy's strategy to catch keys in javascript might be your best choice.

Chris Farmer
A: 

@Chris

A good point, but there are reasons I want it to resize, I want the area it takes up to be the area of the information contained in it. As you can see in the screen shot, if I have a fixed textarea, it takes up a fair wack of vertical space.

I can reduce the font, but I need address to be large and readable. Now I can reduce the size of the text area, but then I have problems with people who have an address line that takes 3 or 4 (one takes 5) lines. Needing to have the user use a scrollbar is a major no-no.

I guess I should be a bit more specific, I'm after vertical resizing, the width doesn't matter as much. The only problem that happens with that, is the ISO number (the large "1") gets pushed under the address when the window width is too small (as you can see on the screenshot).

It's not about having a gimick, it's about having a text field the user can edit that won't take up unnecessary space, but will show all the text in it.

Though if someone comes up with another way to approach the problem I'm open to that too.

Mike
+14  A: 

Facebook does it, when you write on people's walls, but only resizes vertically.

Horizontal resize strikes me as being a terrible mess, due to word-wrap, long lines, and so on, but vertical resize would be pretty safe and nice

Facebook is inhabited by every idiot on the planet, and none of the facebook-using-idiots I know have ever mentioned anything about it or been remotely confused.

I'd use this as anecdotal evidence to say 'go ahead, implement it'

Some javascript to do it, using prototype.js (cos that's what I know)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd"&gt;
<html>
<head>
  <script src="http://www.google.com/jsapi"&gt;&lt;/script&gt;
  <script language="javascript">
  google.load('prototype', '1.6.0.2');
  </script>
</head>
<body>
  <textarea id="text-area" rows="1" cols="50"></textarea>

  <script type="text/javascript" language="javascript">
  resizeIt = function() {
    var str = $('text-area').value;
    var cols = $('text-area').cols;

    var linecount = 0;
    $A(str.split("\n")).each( function(l) {
      linecount += Math.ceil( l.length / cols ); // take into account long lines
    } )
    $('text-area').rows = linecount + 1;
  };

  Event.observe('text-area', 'keydown', resizeIt ); // you could attach to keyUp or whatever if keydown doesn't work
  resizeIt(); //initial on load
  </script>
</body>
</html>

PS: Obviously this javascript is very naive, and you probably don't want to use it on textboxes with novels in them, but you get the general idea

Orion Edwards
+1 for correctly identifying book users.
Picflight
+1  A: 

@Orion

Thanks!

I've modified the code a little because it was acting a little odd, I changed it to activate on keyup, because it wouldn't take into consideration the character that was just typed.

resizeIt = function( ) {
  var str = $( 'iso_address' ).value;
  var cols = $( 'iso_address' ).cols;
  var linecount = 0;
  $A( str.split( "\n" ) ).each( function( l ) {
    linecount += 1 + Math.floor( l.length / cols ); // take into account long lines
  } )
  $( 'iso_address' ).rows = linecount;
};
Mike
+11  A: 

Here's another technique for autosizing a textarea.

  • Uses pixel height instead of line height: more accurate handling of line wrap if a proportional font is used.
  • Accepts either ID or element as input
  • Accepts an optional max height param - useful if you'd rather not let the text area grow beyond a certain size (keep it all on-screen, avoid breaking layout, etc.)
  • Tested on Firefox 3 and IE6

Code: (plain vanilla Javascript)

function FitToContent(id, maxHeight)
{
   var text = id && id.style ? id : document.getElementById(id);
   if ( !text )
      return;

   var adjustedHeight = text.clientHeight;
   if ( !maxHeight || maxHeight > adjustedHeight )
   {
      adjustedHeight = Math.max(text.scrollHeight, adjustedHeight);
      if ( maxHeight )
         adjustedHeight = Math.min(maxHeight, adjustedHeight);
      if ( adjustedHeight > text.clientHeight )
         text.style.height = adjustedHeight + "px";
   }
}

Demo: (uses jQuery, targets on the textarea i'm typing into right now - if you have Firebug installed, paste both samples into the console and test on this page)

$("#post-text").keyup(function() 
{
  FitToContent(this, document.documentElement.clientHeight)
});
Shog9
Nice, but it doesn't "un-grow" when text/line is removed from the textarea..
SMB
@SMB - that probably wouldn't be too hard to implement, just add another conditional
Jason
+2  A: 

Just revisiting this, I've made it a little bit tidier (Though someone who is full bottle on Prototype/Javascript could suggest improvements?).

var TextAreaResize = Class.create();
TextAreaResize.prototype = {
  initialize: function( element, options ) {
    element = $( element );
    this.element = element;

    this.options = Object.extend(
      {},
      options || {} );

    Event.observe( this.element, 'keyup',
      this.onKeyUp.bindAsEventListener( this ) );
    this.onKeyUp();
  },

  onKeyUp: function() {
    // We need this variable because "this" changes in the scope of the
    // function below.
    var cols = this.element.cols;

    var linecount = 0;
    $A( this.element.value.split( "\n" ) ).each( function( l ) {
      // We take long lines into account via the cols divide.
      linecount += 1 + Math.floor( l.length / cols );
    } )

    this.element.rows = linecount;
  }
}

Just call with:

new TextAreaResize( 'textarea_id_name_here' );
Mike
+1  A: 

With jQuery you can use the Auto Growing Textareas plug-in to do this.

However, I had difficulties working with this plug-in when adding dynamic elements to a page. As a result, I found another one (here), and used the first in static fields, and the second on dynamic fields.

Silly, isn't it...

SMB
A: 

I needed this function for myself, but none of the ones from here worked as i needed them.
So I used Orion's code and changed it.

I added in a min height, so that on the destruct it does not get to small.

function resizeIt( id, maxHeight, minHeight ) {
 var text = id && id.style ? id : document.getElementById(id);
 var str = text.value;
 var cols = text.cols;
 var linecount = 0;
 var arStr = str.split( "\n" );
 $( arStr ).each( function( s ) {
  linecount = linecount + 1 + Math.floor( arStr[s].length / cols ); // take into account long lines
 } );
 linecount++;
 linecount = Math.max(minHeight, linecount);
 linecount = Math.min(maxHeight, linecount);
 text.rows = linecount;
};
+2  A: 

Here's a Prototype version of resizing a text area that is not dependent on the number of columns in the textarea. This is a superior technique because it allows you to control the text area via CSS as well as have variable width textarea. Additionally, this version displays the number of characters remaining. While not requested, it's a pretty useful feature and is easily removed if unwanted.

//inspired by: http://github.com/jaz303/jquery-grab-bag/blob/63d7e445b09698272b2923cb081878fd145b5e3d/javascripts/jquery.autogrow-textarea.js
if (window.Widget == undefined) window.Widget = {}; 

Widget.Textarea = Class.create({
  initialize: function(textarea, options)
  {
    this.textarea = $(textarea);
    this.options = $H({
      'min_height' : 30,
      'max_length' : 400
    }).update(options);

    this.textarea.observe('keyup', this.refresh.bind(this));

    this._shadow = new Element('div').setStyle({
      lineHeight : this.textarea.getStyle('lineHeight'),
      fontSize : this.textarea.getStyle('fontSize'),
      fontFamily : this.textarea.getStyle('fontFamily'),
      position : 'absolute',
      top: '-10000px',
      left: '-10000px',
      width: this.textarea.getWidth() + 'px'
    });
    this.textarea.insert({ after: this._shadow });

    this._remainingCharacters = new Element('p').addClassName('remainingCharacters');
    this.textarea.insert({after: this._remainingCharacters});  
    this.refresh();  
  },

  refresh: function()
  { 
    this._shadow.update($F(this.textarea).replace(/\n/g, '<br/>'));
    this.textarea.setStyle({
      height: Math.max(parseInt(this._shadow.getHeight()) + parseInt(this.textarea.getStyle('lineHeight').replace('px', '')), this.options.get('min_height')) + 'px'
    });

    var remaining = this.options.get('max_length') - $F(this.textarea).length;
    this._remainingCharacters.update(Math.abs(remaining)  + ' characters ' + (remaining > 0 ? 'remaining' : 'over the limit'));
  }
});

Create the widget by calling new Widget.Textarea('element_id'). The default options can be overridden by passing them as an object, e.g. new Widget.Textarea('element_id', { max_length: 600, min_height: 50}). If you want to create it for all textareas on the page, do something like:

Event.observe(window, 'load', function() {
 $$('textarea').each(function(textarea) {
   new Widget.Textarea(textarea);
 }); 
});
jeremy
Your technique is superior, well done!
Blaine
A: 

Here's an alternate Prototype implementation: http://gist.github.com/195495

Paul Horsfall
A: 

Here is an extension to the Prototype widget that Jeremy posted on June 4th:

It stops the user from entering more characters if you're using limits in textareas. It checks if there are characters left. If the user copies text into the textarea, the text is cut off at the max. length:

/**
 * Prototype Widget: Textarea
 * Automatically resizes a textarea and displays the number of remaining chars
 * 
 * From: http://stackoverflow.com/questions/7477/autosizing-textarea
 * Inspired by: http://github.com/jaz303/jquery-grab-bag/blob/63d7e445b09698272b2923cb081878fd145b5e3d/javascripts/jquery.autogrow-textarea.js
 */
if (window.Widget == undefined) window.Widget = {}; 

Widget.Textarea = Class.create({
  initialize: function(textarea, options){
    this.textarea = $(textarea);
    this.options = $H({
      'min_height' : 30,
      'max_length' : 400
    }).update(options);

    this.textarea.observe('keyup', this.refresh.bind(this));

    this._shadow = new Element('div').setStyle({
      lineHeight : this.textarea.getStyle('lineHeight'),
      fontSize : this.textarea.getStyle('fontSize'),
      fontFamily : this.textarea.getStyle('fontFamily'),
      position : 'absolute',
      top: '-10000px',
      left: '-10000px',
      width: this.textarea.getWidth() + 'px'
    });
    this.textarea.insert({ after: this._shadow });

    this._remainingCharacters = new Element('p').addClassName('remainingCharacters');
    this.textarea.insert({after: this._remainingCharacters});  
    this.refresh();  
  },

  refresh: function(){ 
    this._shadow.update($F(this.textarea).replace(/\n/g, '<br/>'));
    this.textarea.setStyle({
      height: Math.max(parseInt(this._shadow.getHeight()) + parseInt(this.textarea.getStyle('lineHeight').replace('px', '')), this.options.get('min_height')) + 'px'
    });

    // Keep the text/character count inside the limits:
    if($F(this.textarea).length > this.options.get('max_length')){
      text = $F(this.textarea).substring(0, this.options.get('max_length'));
     this.textarea.value = text;
     return false;
    }

    var remaining = this.options.get('max_length') - $F(this.textarea).length;
    this._remainingCharacters.update(Math.abs(remaining)  + ' characters remaining'));
  }
});
lorem monkey
+2  A: 

One refinement to some of these answers is to let CSS do more of the work.

The basic route seems to be: 1) copy the textarea's contents into a hidden div, 2) let the browser do the work of calculating the height of that div, then 3) set the height of the textarea to that height. Step #3 can be omitted, and the amount of code reduced, by letting CSS handle the sizing of the textarea. Placing both the textarea and the hidden div into the same container, and appropriate use of visibility:hidden, lets the height of the hidden div directly drive the height of the textarea.

<!doctype>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Auto-size TextArea Demo</title>
<script type="text/javascript" src="jquery.js"></script>
<style>
#container {
    position: relative;
}

#textArea {
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    -webkit-box-sizing: border-box;
 font-family: Arial, Helvetica, sans-serif;
    height: 100%;
    overflow: hidden;
    position: absolute;
    width: 100%;
}

#textCopy {
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    -webkit-box-sizing: border-box;
 font-family: Arial, Helvetica, sans-serif;
    padding: 6px;   /* Same border+padding as text area. */
 padding-bottom: 1.15em; /* A bit more than one additional line of text. */ 
    visibility: hidden;
    width: 100%;
}
</style>
<script>
$(function() {
    $(this.textBox)
        .change(autoSize)
        .keydown(autoSize)
        .keyup(autoSize);
 autoSize();
});

function autoSize() {
    // Copy textarea contents; browser will calculate correct height of copy,
    // which will make overall container taller, which will make textarea taller.
 var text = $("#textArea").val().replace(/\n/g, '<br/>');
    $("#textCopy").html(text);
}
</script>
</head>

<body>
<div id="container">
 <textarea id="textArea"></textarea>
 <div id="textCopy"/>
</div>
</body>

</html>
A: 

Internet Explorer users need to remember to explicidly set the line-height value in css. I do a stylesheet that sets the initial properites for all text boxes as as follows.

TEXTAREA { line-height: 14px; font-size: 12px; font-family: arial }

Larry
A: 

Here is a function I just wrote in jQuery to do it - you can port it to prototype but they dont support the "liveness" of jQuery so elements added by Ajax requests will not respond.

This version not only expands but also contracts when delete or backspace is pressed.

This version relies on jquery 1.4.2

Enjoy ;)

http://pastebin.com/SUKeBtnx

Usage $("#sometextarea").textareacontrol(); or (any jquery selector for example) $("textarea").textareacontrol();

Tested on IE7/8, Firefox 3.5, Chrome all works fine.

Alex
+1  A: 

Check the below link: http://james.padolsey.com/javascript/jquery-plugin-autoresize/

$(document).ready(function () {
    $('.ExpandableTextCSS').autoResize({
        // On resize:
        onResize: function () {
            $(this).css({ opacity: 0.8 });
        },
        // After resize:
        animateCallback: function () {
            $(this).css({ opacity: 1 });
        },
        // Quite slow animation:
        animateDuration: 300,
        // More extra space:
        extraSpace:20,
        //Textarea height limit
        limit:10
    });
Gyan
A: 

Here is my solution using jquery

$(document).ready(function() {
    var $abc = $("#abc");
    $abc.css("height", $abc.attr("scrollHeight"));
})

abc is a TextArea

memical
A: 

The last one solution is perfect using jQuery! Cheers!! Thanks!!

Marcos