457

There was another thread about this, which I've tried. But there is one problem: the textarea doesn't shrink if you delete the content. I can't find any way to shrink it to the correct size - the clientHeight value comes back as the full size of the textarea, not its contents.

The code from that page is below:

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";
   }
}

window.onload = function() {
    document.getElementById("ta").onkeyup = function() {
      FitToContent( this, 500 )
    };
}
Racil Hilan
  • 23,737
  • 12
  • 48
  • 51
DisgruntledGoat
  • 66,441
  • 65
  • 202
  • 284
  • 3
    My function does error. It is necessarily to type new line at the end of line. This is better solution. http://james.padolsey.com/javascript/jquery-plugin-autoresize/ –  Mar 17 '11 at 00:06
  • possible duplicate of [Autosizing textarea using prototype](http://stackoverflow.com/questions/7477/autosizing-textarea-using-prototype) – Ciro Santilli Путлер Капут 六四事 Aug 27 '14 at 14:28
  • You can try my plugin for this: https://github.com/AndrewDryga/jQuery.Textarea.Autoresize – Andrew Dryga Jun 14 '13 at 15:48
  • I created a package for this if you are using react: https://www.npmjs.com/package/react-fluid-textarea – Martin Dawson Dec 03 '17 at 00:08
  • Sort and Sweet answer here : https://stackoverflow.com/a/995374/5114465 `function textAreaAdjust(o) { o.style.height = "1px"; o.style.height = (25+o.scrollHeight)+"px"; } ` ` ` – Shurvir Mori Jan 04 '20 at 12:06
  • Using CSS (and a wrapper element) only: https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ – Bergi Feb 09 '22 at 12:47

48 Answers48

547

A COMPLETE YET SIMPLE SOLUTION

Updated 2020-05-14 (Improved browser support for mobiles and tablets)

The following code will work:

  • On key input.
  • With pasted text (right click & ctrl+v).
  • With cut text (right click & ctrl+x).
  • With pre-loaded text.
  • With all textarea's (multiline textbox's) site wide.
  • With Firefox (v31-67 tested).
  • With Chrome (v37-74 tested).
  • With IE (v9-v11 tested).
  • With Edge (v14-v18 tested).
  • With IOS Safari.
  • With Android Browser.
  • With JavaScript strict mode.
  • Is w3c validated.
  • And is streamlined and efficient.

OPTION 1 (With jQuery)

This option requires jQuery and has been tested and is working with 1.7.2 - 3.6

Simple (Add this jquery code to your master script file and forget about it.)

$("textarea").each(function () {
  this.setAttribute("style", "height:" + (this.scrollHeight) + "px;overflow-y:hidden;");
}).on("input", function () {
  this.style.height = "auto";
  this.style.height = (this.scrollHeight) + "px";
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea placeholder="Type, paste, cut text here...">PRELOADED TEXT.
This javascript should now add better support for IOS browsers and Android browsers.</textarea>
<textarea placeholder="Type, paste, cut text here..."></textarea>

Test on jsfiddle


OPTION 2 (Pure JavaScript)

Simple (Add this JavaScript to your master script file and forget about it.)

const tx = document.getElementsByTagName("textarea");
for (let i = 0; i < tx.length; i++) {
  tx[i].setAttribute("style", "height:" + (tx[i].scrollHeight) + "px;overflow-y:hidden;");
  tx[i].addEventListener("input", OnInput, false);
}

function OnInput() {
  this.style.height = "auto";
  this.style.height = (this.scrollHeight) + "px";
}
<textarea placeholder="Type, paste, cut text here...">PRELOADED TEXT. This JavaScript should now add better support for IOS browsers and Android browsers.</textarea>
<textarea placeholder="Type, paste, cut text here..."></textarea>

Test on jsfiddle


OPTION 3 (jQuery Extension)

Useful if you want to apply further chaining to the textareas you want to be auto-sized.

jQuery.fn.extend({
  autoHeight: function () {
    function autoHeight_(element) {
      return jQuery(element)
        .css({ "height": "auto", "overflow-y": "hidden" })
        .height(element.scrollHeight);
    }
    return this.each(function() {
      autoHeight_(this).on("input", function() {
        autoHeight_(this);
      });
    });
  }
});

Invoke with $("textarea").autoHeight()


UPDATING TEXTAREA VIA JAVASCRIPT

When injecting content into a textarea via JavaScript append the following code to invoke the function in option 1.

$("textarea").trigger("input");

PRESET TEXTAREA HEIGHT

To fix the initial height of the textarea you will need to add an additional condition:

const txHeight = 16;
const tx = document.getElementsByTagName("textarea");

for (let i = 0; i < tx.length; i++) {
  if (tx[i].value == '') {
    tx[i].setAttribute("style", "height:" + txHeight + "px;overflow-y:hidden;");
  } else {
    tx[i].setAttribute("style", "height:" + (tx[i].scrollHeight) + "px;overflow-y:hidden;");
  }
  tx[i].addEventListener("input", OnInput, false);
}

function OnInput(e) {
  this.style.height = "auto";
  this.style.height = (this.scrollHeight) + "px";
}
<textarea placeholder="Type, paste, cut text here...">PRELOADED TEXT. This JavaScript should now add better support for IOS browsers and Android browsers.</textarea>
<textarea placeholder="Type, paste, cut text here..."></textarea>
DreamTeK
  • 29,809
  • 22
  • 103
  • 157
  • 13
    This is a great solution, it would be slightly more understandable if the method name and parameter name were updated to be true names instead of individual letters. – Chris Marisic Oct 03 '14 at 15:34
  • It's quite strange that when I use $("#xxx").val("xxx").trigger("input"), it does trigger the input event, however the .scrollHeight attribute still returns 0. I don't understand. lol – WTIFS Aug 30 '16 at 09:44
  • @WTIFS I can't say I noticed that before. Does this impact on what you are trying to achieve? Are you reading the value after the trigger event has completed? You could use a callback function at the end of the function. – DreamTeK Aug 30 '16 at 10:47
  • @Obsidian I had to use Math.max(40, e.scrollHeight) to avoid a 0 height. As for the callback, could you give me more details? I have no idea how to apply a callback here. – WTIFS Aug 30 '16 at 10:56
  • @WTIFS I updated the fiddle to test for scrolleight. If checked during the callback of the input the scroll height reads correctly. See the alert here on input. http://jsfiddle.net/n9uuv0hd/8/ – DreamTeK Aug 30 '16 at 12:23
  • 5
    @Obsidian Thanks! I just figured out why I always get a 0 —— I put the textarea in a Bootstrap modal! As the modal is hidden at first, textarea in it will have a 0 scrollHeight – WTIFS Aug 31 '16 at 04:03
  • 9
    @Obsidian Fantastic!! (Option 3). The `this.style.height = 'auto';` is the magic behavior fix. I was so frustrated why the scrollHeight was so weird in behavior. The height to auto, then matching the scrollHeight in the same render cycle still blows my mind how it 'fixes' this issue though it should only be technically painting the second height. – Modular Sep 29 '16 at 16:49
  • 6
    I tried option 3 in Chrome today and had to make a couple tweaks. 1) It won't always resize down correctly if you have a "height" or "all" transition on the textarea. I'd recommend not using transitions that affect height. 2) The scrollHeight was returning a minumum of two rows worth of height (perhaps because that's the default for textareas in Chrome) but if you change this.style.height = 'auto'; to this.style.height = '0px'; and add a min-height to prevent the initial state from actually going to 0, scrollHeight will correctly return one row of height when appropriate. – ZorroDeLaArena Sep 30 '16 at 14:08
  • @ZorroDeLaArena thanks for noting these points. You are correct the default behaviour for multiline inputs is to show multiple lines even when blank, unless told otherwise. – DreamTeK Oct 03 '16 at 07:42
  • 3
    @Obsidian Can you explain how `this.style.height = 'auto';` fixes the behaviour of scrollHeight? It's just magic. – sidney Aug 25 '17 at 06:05
  • 2
    @sidney Simply: Using `auto` first simply allows the height of the textarea to be reset to the **correct** height of its content before the changes are made using `scrollheight`. – DreamTeK Aug 25 '17 at 08:14
  • Few things: I use the first option in a modal. On mobile, it changes scroll in weird way when typing, which is annoying. Second: Page up/down buttons do not work with this script. –  Sep 03 '17 at 12:25
  • @Soaku Please can you provide an example as I have no issue with either of the points you mention but I want the script to be as effective as possible so if there are issues I would like to fix them. – DreamTeK Sep 04 '17 at 08:08
  • @Obsidian Have you tried using the textara with text spanning more than 100% screen height? Then the first glitch occurs, –  Sep 04 '17 at 08:39
  • @Soaku yes, http://jsfiddle.net/n9uuv0hd/94/ works fine in chrome and operates as textarea home end in firefox. Can you create a fiddle with your problem? – DreamTeK Sep 04 '17 at 09:12
  • @Obsidian Actually, the bug **is** present in your fiddle. When I type, the scroll is "teleported", so the pointer is right in top of the keyboard, when I press "enter", it returns to previous position. –  Sep 04 '17 at 09:24
  • @ZorroDeLaArena Thanks for your contribution. I also needed it to display 1 line when appropriate and this.style.height = '0px' fixed my issue. – Josh Bilderback Sep 18 '17 at 19:45
  • @Obsidian Unfortunately it seems like because of `this.style.height = 'auto';` CSS transitions on height don't work. I can either have transitions working and resizing on deleting lines broken or transitions not working and resizing on deleting lines working. Any workarounds? – sidney Jan 11 '18 at 04:27
  • 1
    @sidney Yes it is a difficulty I have yet to overcome. Although I found that transitioning the height, although looked good was not well received by my customer base. In this case speed over style was better! – DreamTeK Jan 11 '18 at 11:30
  • 1
    elem[0].scrollHeight may help for the case elem.scrollHeight does not work – bbe Jan 29 '18 at 11:41
  • Why is mine not working? Does it work in react too? I include the main.js file in the index.html but it doesn't work somehow. It looks like the function is not being called – Shane Sep 18 '19 at 10:13
  • @KittichoteKamalapirat if you create a new question showing an example of your current code, I would be happy to try and help you with it. For now I would make sure jQuery is initialised before your main.js file. I would then verify that your entire main.js file has no errors because a single missed semi-colon would cascade failure through your document. – DreamTeK Sep 19 '19 at 07:43
  • @DreamTeK Thanks for the response. I have added the question here, please check it out. Let me know if the info I provided was not enough https://stackoverflow.com/questions/58008393/how-to-make-input-fields-height-responsive-to-the-content – Shane Sep 19 '19 at 09:56
  • For some reason my textarea stays at 0px on pageload even with preloaded text. I can see that it works in the jsfiddle example (Option 1) but I have the exact same code in my app and it don't work. :-( It works when I type in the textarea but it doesn't work on page load. – Vincent Jan 02 '20 at 17:39
  • @Vincent Then you must have other conflicting code in your app. This is likely css or Javascript. Try using the browsers web console while running your app to see what is applying the height to the textarea. – DreamTeK Jan 03 '20 at 09:07
  • There still is a use case not answered by your example: if the initial textarea height is not the auto one? An empty `textarea` is two rows height. If I take the jsfiddle of your second example and add an initial `style="height: 33px"` to the empty textarea, you'll see that your code breaks the height of the textarea, making it two rows again... – Fla Apr 24 '20 at 22:19
  • 1
    @fla The code doesn't break it does what it's been programmed to do. Setting an inline style height will be ignored by the script due to specificity. If you wanted a default height for the empty field then you would need to add a caveat in the javascript function to check for empty content and then set the inital height differantly. e.g http://jsfiddle.net/fxre1zm5/ – DreamTeK Apr 29 '20 at 10:56
  • It's not about empty content, it's about one line only textarea. The textarea height is supposed to be the content height. If my content is on one line only, the textarea height should be one line, not two. – Fla May 01 '20 at 11:44
  • @fla An input with type="textarea" is multiline by default however you can change the deaault behaviour using my suggestion. Did this not work for you? – DreamTeK May 04 '20 at 09:25
  • 1
    if anyone has trouble with initial height of textarea just get and set height ONLY after setting overflow hidden $el.style['overflow-y'] = 'hidden'; $el.style.height = $el.scrollHeight + 'px'; – dimson d May 13 '20 at 16:04
  • 2
    This should be the accepted answer. Option 2 works like a charm. – Ulises Vargas De Sousa Jan 09 '21 at 18:24
  • 1
    I used the "pure javascript" one. It works really well, and definitely helped me. Thanks! Funnily enough I couldn't get the other answer's version to work, so I'm quite glad you added this. – RDragonrydr Feb 21 '21 at 19:28
  • 2
    The height with one line of text can be reduced to a single line by setting `rows="1"`. Also, watch out for any answer (including this one) that directly manipulates the height of the textarea to calculate the final height: current versions of iOS Safari have a bug in which scrolling will jump up and down erratically as you enter newlines in a tall textarea as it attempts to keep the textarea in the view. You can use an invisible, second textarea for the calculations to fix this. Also consider that width changes, caused by page size changes, etc, may affect the ideal height. – Chris Sep 09 '21 at 21:12
  • 1
    To avoid the scroll position changing when the textarea height was more than 100% of the window height, I store the pageYOffset at the start of OnInput and then if the pageYOffset is greater after setting the height, then I reset it to the starting pageYOffset. Works a treat. http://jsfiddle.net/L8259hod/ – daamsie Sep 24 '21 at 03:23
  • At least in current Firefox (93), this is really slow because it reads and changes each one, and that forces a reflow for the page. In a related post which I can't find now, someone pointed out that what you want to do is read ALL the scrollHeight values, and then set ALL the style attributes without reading anything. In current Firefox, this strategy is waaaaay faster. – Sam Bayer Oct 19 '21 at 16:05
  • @SamBayer by "really slow" what are you talking about (milliseconds?) because I use this on Firefox everyday on all my applications and the thought it was slow never occurred to me. – DreamTeK Oct 21 '21 at 07:25
  • I have used solution two and it works except when moving between tabs created. I don't know why textareas that are not visible directly on the page is not autosized until I type in them. Does this method not work on textareas not visible from page load? – Yooric Feb 18 '22 at 13:53
  • @Yooric A textarea that is invisble on the page has an initial height of 0. When your tab changes to it the textarea will become visible but the height is still 0. To fix this you need to trigger the routine on tab change. (See UPDATING TEXTAREA VIA JAVASCRIPT part of answer) – DreamTeK Feb 18 '22 at 14:05
  • @DreamTeK I tried that now and it didn't work. I see there is a way to preset the height of the boxes but is there a way to preset it to auto height? Can I modify the "PRESET TEXTAREA HEIGHT" to work with auto height? – Yooric Feb 21 '22 at 09:18
  • 1
    @Yooric The code will work, I came accross the same issue myself previsouly. Create a separate question with your code in and I will see if I can assist. – DreamTeK Feb 21 '22 at 10:19
  • Created a qustion: https://stackoverflow.com/questions/71206689/auto-resize-not-wokring-on-textareas-on-different-tabs @DreamTeK Thanks for taking your so far time. – Yooric Feb 21 '22 at 12:56
272

This works for me (Firefox 3.6/4.0 and Chrome 10/11):

var observe;
if (window.attachEvent) {
    observe = function (element, event, handler) {
        element.attachEvent('on'+event, handler);
    };
}
else {
    observe = function (element, event, handler) {
        element.addEventListener(event, handler, false);
    };
}
function init () {
    var text = document.getElementById('text');
    function resize () {
        text.style.height = 'auto';
        text.style.height = text.scrollHeight+'px';
    }
    /* 0-timeout to get the already changed text */
    function delayedResize () {
        window.setTimeout(resize, 0);
    }
    observe(text, 'change',  resize);
    observe(text, 'cut',     delayedResize);
    observe(text, 'paste',   delayedResize);
    observe(text, 'drop',    delayedResize);
    observe(text, 'keydown', delayedResize);

    text.focus();
    text.select();
    resize();
}
textarea {
    border: 0 none white;
    overflow: hidden;
    padding: 0;
    outline: none;
    background-color: #D0D0D0;
}
<body onload="init();">
<textarea rows="1" style="height:1em;" id="text"></textarea>
</body>

If you want try it on jsfiddle It starts with a single line and grows only the exact amount necessary. It is ok for a single textarea, but I wanted to write something where I would have many many many such textareas (about as much as one would normally have lines in a large text document). In that case it is really slow. (In Firefox it's insanely slow.) So I really would like an approach that uses pure CSS. This would be possible with contenteditable, but I want it to be plaintext-only.

Community
  • 1
  • 1
panzi
  • 7,344
  • 5
  • 39
  • 51
  • This unfortunately fails in IE8, and since it uses overflow: hidden it leaves IE users with no fallback plan. You could toggle overflow on and off to resolve that, but then the scrollbar loses its mind. If you don't use overflow: hidden the textarea doesn't shrink. Close though! – Chris Moschini May 10 '11 at 06:14
  • Using IE tester the code above even works with IE 5.5. I didn't test with IE 9 because I only have XP installed in my VirtualBox. – panzi May 10 '11 at 18:42
  • 13
    It does! Made a jsfiddle for ya: http://jsfiddle.net/CbqFv/ It now works in Chrome, Firefox, and IE8 - although it is a little glitchy in IE8. As you increase or decrease number of lines it freaks out a little. As you might have seen in the autoresize plugin for jQuery, they work around that by cloning the textarea, setting its height to auto instead of the original, then using that to set scrollheight on the original. I did that in this updated fiddle: http://jsfiddle.net/CbqFv/2/ It solves the IE issue but Firefox stops working. – Chris Moschini May 10 '11 at 18:58
  • Thanks, but I see it's using an id='text' to search for the DOM. But I can't use the same Id for more than one textarea, so how do I change it to a class that can be used by others? Thanks again. – Helen Neely Feb 18 '12 at 08:45
  • 3
    @HelenNeely: Then don't use an id. E.g. use a class and do what init does for all elements of that class. That should be a beginners task. – panzi Feb 23 '12 at 04:28
  • 1
    If you want to target modern browsers, you don't need to attach the handler to all those events. Just attach to `input` or `oninput` event. – Denilson Sá Maia Sep 20 '12 at 18:40
  • 3
    @DenilsonSá If one could only assume that only modern browsers are used the web would be a better place. – panzi Sep 22 '12 at 01:52
  • Does this work if I have multiple textboxes that require the feature? – user3388884 Mar 26 '14 at 20:39
  • 2
    This won't work well if a scrollbar is envolved in IE11. If you use this fiddle http://jsfiddle.net/CbqFv/ and enter lines until you've got a scrollbar you will see it. Any ideas? – kaljak Aug 25 '15 at 13:05
  • kaljak, does adding `overflow: hidden;` help? Edit: Wait, that is already there. Then no idea. – panzi Aug 25 '15 at 14:37
  • What can I do for prevent scroll bar flashing? If the style `overflow:auto;` – ollazarev Apr 08 '16 at 23:12
  • @ollazarev Maybe use an `visibility:hidden;position:fixed;` textarea/div to measure the height? Or use a `contentEditable="true"`-div instead of a textarea (which complicates other things heavily). – panzi Apr 09 '16 at 19:02
  • 9
    This breaks completely if the textarea is near the bottom page end - everything jumps back and forward due to `style.height='auto'`. I suspect the solution is to add a hidden sibling used only for measurement. – rr- Jun 12 '16 at 09:03
  • would it hang the browser if pasted large text inside like 3MB file after uploading and getting the response back into the textarea – Muhammad Omer Aslam Sep 11 '19 at 10:28
  • On Firefox 70 it doesn't resize on first character that overflows. On second char it gets resized. – cdalxndr Dec 02 '19 at 21:15
  • This works fine on JSFuddle, but I'm wondering (as I have such task to do) how it does behave if you increasing and decreasing the font size from the browser settings. Also across different languages? – Vladyn May 28 '20 at 07:54
68

jQuery solution adjust the css to match your requirements

css...

div#container textarea {
    min-width: 270px;
    width: 270px;
    height: 22px;
    line-height: 24px;
    min-height: 22px;
    overflow-y: hidden; /* fixes scrollbar flash - kudos to @brettjonesdev */
    padding-top: 1.1em; /* fixes text jump on Enter keypress */
}

javascript...

// auto adjust the height of
$('#container').delegate( 'textarea', 'keydown', function (){
    $(this).height( 0 );
    $(this).height( this.scrollHeight );
});
$('#container').find( 'textarea' ).keydown();

OR alternative for jQuery 1.7+...

// auto adjust the height of
$('#container').on( 'keyup', 'textarea', function (){
    $(this).height( 0 );
    $(this).height( this.scrollHeight );
});
$('#container').find( 'textarea' ).keyup();

I've created a fiddle with the absolute minimum styling as a starting point for your experiments... http://jsfiddle.net/53eAy/951/

Ted
  • 590
  • 4
  • 22
chim
  • 8,095
  • 3
  • 49
  • 59
  • 5
    This is good, but I would add `overflow-y: hidden;` to the css to prevent the scroll bar from briefly flashing when resize occurs. – brettjonesdev May 06 '13 at 15:11
  • Not entirely sure, but without it the text area grows by too much. I think it's to do with the way that the elements scrollHeight is calculated. – chim Jun 21 '13 at 09:31
  • I like this solution a lot, in the first example, how could I limit the amount of rows and get rid of the padding? – Darius Sep 21 '13 at 07:12
  • Hi Darius, not sure about getting rid of the padding, tried all kinds of tricks on this, but it's either the padding or the text jump :(. There must be a solution so I'll keep trying. The top answer works well with no padding. Try one of the fiddles in the @ChrisMoschini comment. – chim Sep 23 '13 at 10:02
  • @Darius I'd limit the max rows in the javascript, add the line `if ($(this).height() > maxHeight) { return; } somewhere near the top. You could set overflow-y back to auto to re-instate the scrollbars. You'd need to trigger the function on page load if you conditionally add the scrollbars in. – chim Sep 23 '13 at 10:18
  • 1
    Use multiple events to make work for wide verity of scenario `$('textarea.auto-resize').on('change cut paste drop keyup', function(){...})` – raxith May 04 '14 at 11:43
  • 1
    Jumps around like crazy in Chrome 40 Win 8.1 on texts longer than the viewport. Renders it quite unusable. – tobibeer Feb 11 '15 at 13:59
  • 1
    Holding enter (newline) results in scrolling. The size does not adjust until the key is released. – Niels Abildgaard Apr 23 '15 at 17:03
33
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Textarea autoresize</title>
    <style>
    textarea {
        overflow: hidden;
    }
    </style>
    <script>
    function resizeTextarea(ev) {
        this.style.height = '24px';
        this.style.height = this.scrollHeight + 12 + 'px';
    }

    var te = document.querySelector('textarea');
    te.addEventListener('input', resizeTextarea);
    </script>
</head>
<body>
    <textarea></textarea>
</body>
</html>

Tested in Firefox 14 and Chromium 18. The numbers 24 and 12 are arbitrary, test to see what suits you best.

You could do without the style and script tags, but it becomes a bit messy imho (this is old style HTML+JS and is not encouraged).

<textarea style="overflow: hidden" onkeyup="this.style.height='24px'; this.style.height = this.scrollHeight + 12 + 'px';"></textarea>

Edit: modernized code. Changed onkeyup attribute to addEventListener.
Edit: keydown works better than keyup
Edit: declare function before using
Edit: input works better than keydown (thnx @WASD42 & @MA-Maddin)

jsfiddle

GijsjanB
  • 9,790
  • 4
  • 22
  • 26
  • 3
    This one has some drawbacks: 1) Textarea would not be resized then user paste something with his mouse buttons only; 2) If the user will paste something using keyboard (Ctrl + V) the textarea would be resized only when he will release Ctrl. If the user will press Ctrl + V several times then the textarea will grow only after the last one. You should add same function to paste, cut, change and drop events too. – WASD42 Sep 24 '13 at 14:10
  • So just use the `input` event instead. See http://stackoverflow.com/a/14029861/1951524 – Martin Schneider Nov 22 '16 at 20:52
  • Improved version with multiple `textarea` support as a one-liner: `document.querySelectorAll('textarea').forEach(function(te){ te.addEventListener("input", function() { this.style.height='24px'; this.style.height=this.scrollHeight+12+'px'; }); });` – Meisner Jan 29 '17 at 01:01
  • 1
    @Meisner It's shorter, but I wouldn't say it's "improved". With an anonymous function, there is no way to call `removeEventListener` and clean up your event listeners. This is especially important when creating event listeners all over the place in a forEach loop. Plus, in my view, readability is far more important than conciseness. – GijsjanB Mar 09 '17 at 09:52
  • 1
    for others and future me, ensure that `box-sizing: border-box;` property is set or else the textarea explands by 4px for each onInput event. I spent like 3 hours until i figured out this was the problem. – AIon Feb 12 '19 at 19:53
30

The best solution (works and is short) for me is:

    $(document).on('input', 'textarea', function () {
        $(this).outerHeight(38).outerHeight(this.scrollHeight); // 38 or '1em' -min-height
    }); 

It works like a charm without any blinking with paste (with mouse also), cut, entering and it shrinks to the right size.

Please take a look at jsFiddle.

vatavale
  • 1,326
  • 18
  • 28
  • Terrific!!! one line deal! silly question ho I can add an animation when the it resizes? like the text box sorta gently resizing rather then the jump to size? – user2513846 Mar 22 '16 at 06:07
  • 1
    @user2513846 Looks like it's not possible by the design of multi line editing. I made an example of animating textarea size: https://jsfiddle.net/yk2g333e/ As you know text cursor goes down at the and of line or caret, so if there is no pause at this moment there is nothing to animate. But pausing will be to annoying for user. – vatavale Mar 23 '16 at 11:32
  • 1
    @user2513846 However, with one more empty line at the end of textarea, it's possible to have smooth animation: https://jsfiddle.net/p23erdfr/ – vatavale Mar 23 '16 at 12:15
  • Useful tweak for this solution is to also hide vertical scroll-bar (seen in jsFiddle, not mentioned here), you do this with inline styling (style="overflow:hidden") or proper one with: textarea {overflow: hidden;} – Gregor Simončič Oct 03 '20 at 17:21
  • @Gregor Simončič Yes, especially for a long text -- it gives right calculation of the textarea target height. And also, it’s better for precise calculation to set outerHeight to 0. – vatavale Oct 04 '20 at 14:22
19

If you don’t need to support IE8 you can use the input event:

var resizingTextareas = [].slice.call(document.querySelectorAll('textarea[autoresize]'));

resizingTextareas.forEach(function(textarea) {
  textarea.addEventListener('input', autoresize, false);
});

function autoresize() {
  this.style.height = 'auto';
  this.style.height = this.scrollHeight+'px';
  this.scrollTop = this.scrollHeight;
  window.scrollTo(window.scrollLeft,(this.scrollTop+this.scrollHeight));
}

Now you only need to add some CSS and you are done:

textarea[autoresize] {
  display: block;
  overflow: hidden;
  resize: none;
}

Usage:

<textarea autoresize>Type here and I’ll resize.</textarea>

You can read more about how it works on my blog post.

Max Hoffmann
  • 2,859
  • 1
  • 15
  • 11
  • 1
    Nice solution, but need little fix. When content of textarea is loaded f.ex. from database, textarea stay small. You have to trigger `input` event during initialization in foreach cycle. – Ajax Feb 09 '16 at 14:27
19

Found an one liner from here;

<textarea name="text" oninput="this.style.height = ''; this.style.height = this.scrollHeight +'px'"></textarea>
K-Gun
  • 10,689
  • 2
  • 52
  • 56
17K
  • 221
  • 2
  • 4
17

You're using the higher value of the current clientHeight and the content scrollHeight. When you make the scrollHeight smaller by removing content, the calculated area can't get smaller because the clientHeight, previously set by style.height, is holding it open. You could instead take a max() of scrollHeight and a minimum height value you have predefined or calculated from textarea.rows.

In general you probably shouldn't really rely on scrollHeight on form controls. Apart from scrollHeight being traditionally less widely-supported than some of the other IE extensions, HTML/CSS says nothing about how form controls are implemented internally and you aren't guaranteed scrollHeight will be anything meaningful. (Traditionally some browsers have used OS widgets for the task, making CSS and DOM interaction on their internals impossible.) At least sniff for scrollHeight/clientHeight's existance before trying to enable the effect.

Another possible alternative approach to avoid the issue if it's important that it work more widely might be to use a hidden div sized to the same width as the textarea, and set in the same font. On keyup, you copy the text from the textarea to a text node in hidden div (remembering to replace '\n' with a line break, and escape '<'/'&' properly if you're using innerHTML). Then simply measuring the div's offsetHeight will give you the height you need.

bobince
  • 514,530
  • 102
  • 640
  • 820
  • 1
    Which browsers are supposed to have problems with `scrollHeight` for textareas? Works fine with current versions of IE, Firefox and Opera... – Christoph Jan 18 '09 at 09:54
  • 1
    The only one I have to hand is Konqueror, which returns the clientHeight as scrollHeight. This is a behaviour you can expect unless the browser applies the CSS box model to the internals of widgets, something they haven't always done and the standard doesn't say they have to. – bobince Jan 18 '09 at 12:38
14

autosize

https://github.com/jackmoore/autosize

Just works, standalone, is popular (3.0k+ GitHub stars as of October 2018), available on cdnjs) and lightweight (~3.5k). Demo:

<textarea id="autosize" style="width:200px;">a
J   b
c</textarea>
<script src="https://cdnjs.cloudflare.com/ajax/libs/autosize.js/4.0.2/autosize.min.js"></script>
<script>autosize(document.querySelectorAll('#autosize'));</script>

BTW, if you are using the ACE editor, use maxLines: Infinity: Automatically adjust height to contents in Ace Cloud 9 editor

7

Has anyone considered contenteditable? No messing around with scrolling,a nd the only JS I like about it is if you plan on saving the data on blur... and apparently, it's compatible on all of the popular browsers : http://caniuse.com/#feat=contenteditable

Just style it to look like a text box, and it autosizes... Make its min-height the preferred text height and have at it.

What's cool about this approach is that you can save and tags on some of the browsers.

http://jsfiddle.net/gbutiri/v31o8xfo/

var _auto_value = '';
$(document).on('blur', '.autosave', function(e) {
  var $this = $(this);
  if ($this.text().trim() == '') {
    $this.html('');
  }

  // The text is here. Do whatever you want with it.
  $this.addClass('saving');

  if (_auto_value !== $this.html() || $this.hasClass('error')) {

    // below code is for example only.
    $.ajax({
      url: '/echo/json/?action=xyz_abc',
      data: 'data=' + $this.html(),
      type: 'post',
      datatype: 'json',
      success: function(d) {
        console.log(d);
        $this.removeClass('saving error').addClass('saved');
        var k = setTimeout(function() {
          $this.removeClass('saved error')
        }, 500);
      },
      error: function() {
        $this.removeClass('saving').addClass('error');
      }
    });
  } else {
    $this.removeClass('saving');
  }
}).on('focus mouseup', '.autosave', function() {
  var $this = $(this);
  if ($this.text().trim() == '') {
    $this.html('');
  }
  _auto_value = $this.html();
}).on('keyup', '.autosave', function(e) {
  var $this = $(this);
  if ($this.text().trim() == '') {
    $this.html('');
  }
});
body {
  background: #3A3E3F;
  font-family: Arial;
}

label {
  font-size: 11px;
  color: #ddd;
}

.autoheight {
  min-height: 16px;
  font-size: 16px;
  margin: 0;
  padding: 10px;
  font-family: Arial;
  line-height: 20px;
  box-sizing: border-box;
  -o-box-sizing: border-box;
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  overflow: hidden;
  display: block;
  resize: none;
  border: 0;
  outline: none;
  min-width: 200px;
  background: #ddd;
  max-height: 400px;
  overflow: auto;
}

.autoheight:hover {
  background: #eee;
}

.autoheight:focus {
  background: #fff;
}

.autosave {
  -webkit-transition: all .2s;
  -moz-transition: all .2s;
  transition: all .2s;
  position: relative;
  float: none;
}

.autoheight * {
  margin: 0;
  padding: 0;
}

.autosave.saving {
  background: #ff9;
}

.autosave.saved {
  background: #9f9;
}

.autosave.error {
  background: #f99;
}

.autosave:hover {
  background: #eee;
}

.autosave:focus {
  background: #fff;
}

[contenteditable=true]:empty:before {
  content: attr(placeholder);
  color: #999;
  position: relative;
  top: 0px;
  /*
    For IE only, do this:
    position: absolute;
    top: 10px;
    */
  cursor: text;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label>Your Name</label>
<div class="autoheight autosave contenteditable" contenteditable="true" placeholder="Your Name"></div>
G-Cyrillus
  • 94,270
  • 13
  • 95
  • 118
Webmaster G
  • 482
  • 5
  • 11
  • Chrome will add
    elements on ENTER.
    – sbaechler Nov 04 '16 at 18:36
  • `contenteditable` is good, but if you want plaintext it needs a bunch of options like `-webkit-user-modify: read-write-plaintext-only` and `white-space: pre-wrap`. – mik01aj Dec 12 '17 at 11:02
  • @_sbaechler: the
    s are omitted when using `[dom-element].innerText`. @WebmasterG IMHO it would be nice to omit jquery (for transparency) and to include the example on this page via runnable Code Snippet instead of on jsfiddle.
    – adabru Aug 06 '18 at 08:46
7

As a different approach, you can use a <span> which adjusts its size automatically. You will need make it editable by adding the contenteditable="true" property and you're done:

div {
  width: 200px;
}

span {
  border: 1px solid #000;
  padding: 5px;
}
<div>
  <span contenteditable="true">This text can be edited by the user</span>
</div>

The only issue with this approach is that if you want to submit the value as part of the form, you'll have to do so by yourself in JavaScript. Doing so is relatively easy. For example, you can add a hidden field and in the onsubmit event of the form assign the value of the span to the hidden field which will be then automatically submitted with the form.

Racil Hilan
  • 23,737
  • 12
  • 48
  • 51
  • There is already an answer using contenteditable on this page: https://stackoverflow.com/questions/454202/creating-a-textarea-with-auto-resize/26413009#26413009 – adabru Aug 06 '18 at 08:42
  • @adabru Yes, but the two answers are not the same. Toyota makes red cars, and Ford makes red cars too, but they are different, aren't they? If you like the other answer more, then go with it. But there are those who appreciate my answer more and go with it. Having different options is good. – Racil Hilan Aug 10 '18 at 06:59
6

There is a slightly different approach.

<div style="position: relative">
  <pre style="white-space: pre-wrap; word-wrap: break-word"></pre>
  <textarea style="position: absolute; top: 0; left: 0; width: 100%; height: 100%"></textarea>
</div>

The idea is to copy the text from textarea into the pre and let CSS make sure that they have the same size.

The benefit is that frameworks present simple tools to move text around without touching any events. Namely, in AngularJS you would add a ng-model="foo" ng-trim="false" to the textarea and ng-bind="foo + '\n'" to the pre. See a fiddle.

Just make sure that pre has the same font size as the textarea.

Karolis Juodelė
  • 3,658
  • 1
  • 18
  • 32
  • 1
    "_Just make sure that `pre` has the same font size as the `textarea`_" - They need the same `line-height`, and an equal amount of `padding` and/or `border` too. This is the best solution for Angular. – Jamie Barker Jan 20 '16 at 15:39
  • Simple best solution I've seen and tried to come up with for reactive update. Thanks! – Alex Shink Nov 23 '20 at 18:52
5

The following works for cutting, pasting, etc., regardless of whether those actions are from the mouse, a keyboard shortcut, selecting an option from a menu bar ... several answers take a similar approach but they don't account for box-sizing, which is why they incorrectly apply the style overflow: hidden.

I do the following, which also works well with max-height and rows for minimum and maximum height.

function adjust() {
  var style = this.currentStyle || window.getComputedStyle(this);
  var boxSizing = style.boxSizing === 'border-box'
      ? parseInt(style.borderBottomWidth, 10) +
        parseInt(style.borderTopWidth, 10)
      : 0;
  this.style.height = '';
  this.style.height = (this.scrollHeight + boxSizing) + 'px';
};

var textarea = document.getElementById("ta");
if ('onpropertychange' in textarea) { // IE
  textarea.onpropertychange = adjust;
} else if ('oninput' in textarea) {
  textarea.oninput = adjust;
}
setTimeout(adjust.bind(textarea));
textarea {
  resize: none;
  max-height: 150px;
  border: 1px solid #999;
  outline: none;
  font: 18px sans-serif;
  color: #333;
  width: 100%;
  padding: 8px 14px;
  box-sizing: border-box;
}
<textarea rows="3" id="ta">
Try adding several lines to this.
</textarea>

For absolute completeness, you should call the adjust function in a few more circumstances:

  1. Window resize events, if the width of the textarea changes with window resizing, or other events that change the width of the textarea
  2. When the textarea's display style attribute changes, e.g. when it goes from none (hidden) to block
  3. When the value of the textarea is changed programmatically

Note that using window.getComputedStyle or getting currentStyle can be somewhat computationally expensive, so you may want to cache the result instead.

Works for IE6, so I really hope that's good enough support.

Joseph Nields
  • 5,233
  • 2
  • 29
  • 46
5

I used the following code for multiple textareas. Working fine in Chrome 12, Firefox 5 and IE 9, even with delete, cut and paste actions performed in the textareas.

function attachAutoResizeEvents() {
  for (i = 1; i <= 4; i++) {
    var txtX = document.getElementById('txt' + i)
    var minH = txtX.style.height.substr(0, txtX.style.height.indexOf('px'))
    txtX.onchange = new Function("resize(this," + minH + ")")
    txtX.onkeyup = new Function("resize(this," + minH + ")")
    txtX.onchange(txtX, minH)
  }
}

function resize(txtX, minH) {
  txtX.style.height = 'auto' // required when delete, cut or paste is performed
  txtX.style.height = txtX.scrollHeight + 'px'
  if (txtX.scrollHeight <= minH)
    txtX.style.height = minH + 'px'
}
window.onload = attachAutoResizeEvents
textarea {
  border: 0 none;
  overflow: hidden;
  outline: none;
  background-color: #eee
}
<textarea style='height:100px;font-family:arial' id="txt1"></textarea>
<textarea style='height:125px;font-family:arial' id="txt2"></textarea>
<textarea style='height:150px;font-family:arial' id="txt3"></textarea>
<textarea style='height:175px;font-family:arial' id="txt4"></textarea>
G-Cyrillus
  • 94,270
  • 13
  • 95
  • 118
Nikunj Bhatt
  • 188
  • 4
  • 15
4

Here is an angularjs directive for panzi's answer.

 module.directive('autoHeight', function() {
        return {
            restrict: 'A',
            link: function(scope, element, attrs) {
                element = element[0];
                var resize = function(){
                    element.style.height = 'auto';
                    element.style.height = (element.scrollHeight)+'px';
                };
                element.addEventListener('change', resize, false);
                element.addEventListener('cut',    resize, false);
                element.addEventListener('paste',  resize, false);
                element.addEventListener('drop',   resize, false);
                element.addEventListener('keydown',resize, false);

                setTimeout(resize, 100);
            }
        };
    });

HTML:

<textarea ng-model="foo" auto-height></textarea>
chrmcpn
  • 554
  • 6
  • 10
  • You should rather use $timeout than setTimout - this will make sure that the model gets initialised (no need to specify milliseconds - it will get executed after Angular digest cycle). Also element = element[0]; is a very bad practise... Anyway, the solution is no good for me as I need the textarea to be one-line untill there's more text. – Karol Websky Dec 12 '17 at 13:23
4

A bit corrections. Works perfectly in Opera

  $('textarea').bind('keyup keypress', function() {
      $(this).height('');
      var brCount = this.value.split('\n').length;
      this.rows = brCount+1; //++ To remove twitching
      var areaH = this.scrollHeight,
          lineHeight = $(this).css('line-height').replace('px',''),
          calcRows = Math.floor(areaH/lineHeight);
      this.rows = calcRows;
  });
artnik-pro
  • 49
  • 2
3

I know a short and correct way of implementing this with jquery.No extra hidden div needed and works in most browser

<script type="text/javascript">$(function(){
$("textarea").live("keyup keydown",function(){
var h=$(this);
h.height(60).height(h[0].scrollHeight);//where 60 is minimum height of textarea
});});

</script>
  • jQuery's `.live()` has been deprecated. http://api.jquery.com/live/ You'll generally want to use `.on()` instead. – Luke Jun 27 '14 at 22:22
3

An even simpler, cleaner approach is this:

// adjust height of textarea.auto-height
$(document).on( 'keyup', 'textarea.auto-height', function (e){
    $(this).css('height', 'auto' ); // you can have this here or declared in CSS instead
    $(this).height( this.scrollHeight );
}).keyup();

// and the CSS

textarea.auto-height {
    resize: vertical;
    max-height: 600px; /* set as you need it */
    height: auto;      /* can be set here of in JS */
    overflow-y: auto;
    word-wrap:break-word
}

All that is needed is to add the .auto-height class to any textarea you want to target.

Tested in FF, Chrome and Safari. Let me know if this doesn't work for you, for any reason. But, this is the cleanest and simplest way I've found this to work. And it works great! :D

Eric Platon
  • 9,266
  • 6
  • 40
  • 47
revive
  • 801
  • 2
  • 14
  • 29
3

I Don't know if anyone mention this way but in some cases it's possible to resize the height with rows Attribute

textarea.setAttribute('rows',breaks);

Demo

h0mayun
  • 3,368
  • 28
  • 39
  • 1
    That works only if you have `white-space: nowrap;`. When a line wraps to another line without line-break, the textarea will not be adjusted properly anymore. – Yeti Aug 10 '17 at 11:08
3

You can use JQuery to expand the textarea while typing:

$(document).find('textarea').each(function () {
  var offset = this.offsetHeight - this.clientHeight;

  $(this).on('keyup input focus', function () {
    $(this).css('height', 'auto').css('height', this.scrollHeight + offset);
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div>
<textarea name="note"></textarea>
<div>
Mauricio Sánchez
  • 3,632
  • 1
  • 19
  • 15
  • Cleanest solution. but as we should stop using jQuery: textarea.addEventListener('keyup', () => { textarea.style.height = 0; textarea.style.height = `${textarea.scrollHeight}px`; }); – Toni Michel Caubet Feb 06 '19 at 10:30
3

Those who want to achieve the same in new versions of Angular.

Grab textArea elementRef.

@ViewChild('textArea', { read: ElementRef }) textArea: ElementRef;

public autoShrinkGrow() {
    textArea.style.overflow = 'hidden';
    textArea.style.height = '0px';
    textArea.style.height = textArea.scrollHeight + 'px';
}

<textarea (keyup)="autoGrow()" #textArea></textarea>

I am also adding another use case that may come handy some users reading the thread, when user want to increase the height of text-area to certain height and then have overflow:scroll on it, above method can be extended to achieve the mentioned use-case.

  public autoGrowShrinkToCertainHeight() {
    const textArea = this.textArea.nativeElement;
    if (textArea.scrollHeight > 77) {
      textArea.style.overflow = 'auto';
      return;
    }
    else {
      textArea.style.overflow = 'hidden';
      textArea.style.height = '0px';
      textArea.style.height = textArea.scrollHeight + 'px';
    }
  }
Divyanshu Rawat
  • 3,569
  • 1
  • 33
  • 42
2

Some of the answers here don't account for padding.

Assuming you have a maxHeight you don't want to go over, this worked for me:

    // obviously requires jQuery

    // element is the textarea DOM node

    var $el = $(element);
    // inner height is height + padding
    // outerHeight includes border (and possibly margins too?)
    var padding = $el.innerHeight() - $el.height();
    var originalHeight = $el.height();

    // XXX: Don't leave this hardcoded
    var maxHeight = 300;

    var adjust = function() {
        // reset it to the original height so that scrollHeight makes sense
        $el.height(originalHeight);

        // this is the desired height (adjusted to content size)
        var height = element.scrollHeight - padding;

        // If you don't want a maxHeight, you can ignore this
        height = Math.min(height, maxHeight);

        // Set the height to the new adjusted height
        $el.height(height);
    }

    // The input event only works on modern browsers
    element.addEventListener('input', adjust);
hasen
  • 155,371
  • 64
  • 187
  • 227
2

This code works for pasting and select delete also.

onKeyPressTextMessage = function(){
   var textArea = event.currentTarget;
     textArea.style.height = 'auto';
     textArea.style.height = textArea.scrollHeight + 'px';
};
<textarea onkeyup="onKeyPressTextMessage(event)" name="welcomeContentTmpl" id="welcomeContent" onblur="onblurWelcomeTitle(event)" rows="2" cols="40" maxlength="320"></textarea>

Here is the JSFiddle

Kurenai Kunai
  • 1,826
  • 2
  • 11
  • 22
2

I recommend the javascript library from http://javierjulio.github.io/textarea-autosize.

Per comments, add example codeblock on plugin usage:

<textarea class="js-auto-size" rows="1"></textarea>

<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script src="jquery.textarea_autosize.min.js"></script>
<script>
$('textarea.js-auto-size').textareaAutoSize();
</script>

Minimum required CSS:

textarea {
  box-sizing: border-box;
  max-height: 160px; // optional but recommended
  min-height: 38px;
  overflow-x: hidden; // for Firefox (issue #5)
}
trh
  • 7,026
  • 1
  • 25
  • 39
orange01
  • 1,474
  • 14
  • 26
2

None of the answers seem to work. But this one works for me: https://coderwall.com/p/imkqoq/resize-textarea-to-fit-content

$('#content').on( 'change keyup keydown paste cut', 'textarea', function (){
    $(this).height(0).height(this.scrollHeight);
}).find( 'textarea' ).change();
karoluS
  • 2,593
  • 2
  • 21
  • 38
Kim Homann
  • 2,752
  • 1
  • 15
  • 19
2

MakeTextAreaResisable that uses qQuery

function MakeTextAreaResisable(id) {
    var o = $(id);
    o.css("overflow-y", "hidden");

    function ResizeTextArea() {
        o.height('auto');
        o.height(o[0].scrollHeight);
    }

    o.on('change', function (e) {
        ResizeTextArea();
    });

    o.on('cut paste drop keydown', function (e) {
        window.setTimeout(ResizeTextArea, 0);
    });

    o.focus();
    o.select();
    ResizeTextArea();
}
Igor Krupitsky
  • 717
  • 5
  • 9
2

my implementation is very simple, count the number of lines in the input (and minimum 2 rows to show that it's a textarea):

textarea.rows = Math.max(2, textarea.value.split("\n").length) // # oninput

full working example with stimulus: https://jsbin.com/kajosolini/1/edit?html,js,output

(and this works with the browser's manual resize handle for instance)

localhostdotdev
  • 1,531
  • 13
  • 20
  • 3
    This only works if you break the line by pressing "enter". If you text is longer than the textarea's width but you don't hit "enter", it doesn't expand. – Nathalia Xavier Jul 24 '19 at 09:05
2

Accepted answer is working fine. But that is lot of code for this simple functionality. The below code will do the trick.

   $(document).on("keypress", "textarea", function (e) {
    var height = $(this).css("height");
    var iScrollHeight = $(this).prop("scrollHeight");
    $(this).css('height',iScrollHeight);
    });
1

Here is what I did while using MVC HTML Helper for TextArea. I had quite a few of textarea elements so had to distinguish them using Model Id.

 @Html.TextAreaFor(m => m.Text, 2, 1, new { id = "text" + Model.Id, onkeyup = "resizeTextBox(" + Model.Id + ");" })

and in script added this:

   function resizeTextBox(ID) {            
        var text = document.getElementById('text' + ID);
        text.style.height = 'auto';
        text.style.height = text.scrollHeight + 'px';            
    }

I have tested it on IE10 and Firefox23

gunnerz
  • 1,860
  • 5
  • 24
  • 36
1

If scrollHeight could be trusted, then:

textarea.onkeyup=function() {
  this.style.height='';
  this.rows=this.value.split('\n').length;
  this.style.height=this.scrollHeight+'px';
}
rrr
  • 35
  • 1
  • 4
    this.rows=this.value.split('\n').length; This wouldn't work. Because number of lines in the text is not equal to count of \n. You haven't considered textwrap. – Eastern Monk Mar 13 '12 at 17:44
  • @EasternMonk if you try it it actually works well and is quite intuitive IMHO: https://jsbin.com/tilokafasa/1/edit?html,js,output – localhostdotdev May 09 '19 at 15:57
1

The Best way I found:

$("textarea.auto-grow").each( function(){
    $(this).keyup(function(){
        $(this).height( $(this)[0].scrollHeight - Number( $(this).css("font-size").replace("px", "") ) );
    });
});

Other ways has a font-size bug.

Thats why this is the best.

Karl Zillner
  • 560
  • 7
  • 16
1

for Angular 2+, just do this

<textarea (keydown)="resize($event)"></textarea>


resize(e) {
    setTimeout(() => {
      e.target.style.height = 'auto';
      e.target.style.height = (e.target.scrollHeight)+'px';
    }, 0);
  }

textarea {
  resize: none;
  overflow: hidden;
}
Johansrk
  • 4,984
  • 3
  • 34
  • 34
1

An example implementation with React:

const {
  useLayoutEffect,
  useState,
  useRef
} = React;

const TextArea = () => {
  const ref = useRef();
  const [value, setValue] = useState('Some initial text that both wraps and uses\nnew\nlines');

  // This only tracks the auto-sized height so we can tell if the user has manually resized
  const autoHeight = useRef();

  useLayoutEffect(() => {
    if (!ref.current) {
      return;
    }

    if (
      autoHeight.current !== undefined &&
      ref.current.style.height !== autoHeight.current
    ) {
      // don't auto size if the user has manually changed the height
      return;
    }

    ref.current.style.height = "auto";
    ref.current.style.overflow = "hidden";
    const next = `${ref.current.scrollHeight}px`;
    ref.current.style.height = next;
    autoHeight.current = next;
    ref.current.style.overflow = "auto";
  }, [value, ref, autoHeight]);


  return (
    <textarea
      ref={ref}
      style={{
        resize: 'vertical',
        minHeight: '1em',
      }}
      value={value}
      onChange={event => setValue(event.target.value)}
    />
  );
}

ReactDOM.render(<TextArea />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Chris
  • 1,904
  • 24
  • 36
0

For those who want the textarea to be auto resized on both width and height:

HTML:

<textarea class='textbox'></textarea>
<div>
  <span class='tmp_textbox'></span>
</div>

CSS:

.textbox,
.tmp_textbox {
  font-family: 'Arial';
  font-size: 12px;
  resize: none;
  overflow:hidden;
}

.tmp_textbox {
  display: none;
}

jQuery:

$(function(){
  //alert($('.textbox').css('padding'))
  $('.textbox').on('keyup change', checkSize)
  $('.textbox').trigger('keyup')

  function checkSize(){
    var str = $(this).val().replace(/\r?\n/g, '<br/>');
    $('.tmp_textbox').html( str )
    console.log($(this).val())

    var strArr = str.split('<br/>')
    var row = strArr.length
    $('.textbox').attr('rows', row)
    $('.textbox').width( $('.tmp_textbox').width() + parseInt($('.textbox').css('padding')) * 2 + 10 )
  }
})

Codepen:

http://codepen.io/anon/pen/yNpvJJ

Cheers,

Goon Nguyen
  • 1,427
  • 11
  • 26
0

The jQuery solution is to set the height of the textarea to 'auto', check the scrollHeight and then adapt the height of the textarea to that, every time a textarea changes (JSFiddle):

$('textarea').on( 'input', function(){
    $(this).height( 'auto' ).height( this.scrollHeight );
});

If you're dynamically adding textareas (through AJAX or whatever), you can add this in your $(document).ready to make sure all textareas with class 'autoheight' are kept to the same height as their content:

$(document).on( 'input', 'textarea.autoheight', function() {
    $(this).height( 'auto' ).height( this.scrollHeight );
});

Tested and working in Chrome, Firefox, Opera and IE. Also supports cut and paste, long words, etc.

patrick
  • 11,118
  • 7
  • 64
  • 78
  • If you're loading a text-area with initial content through AJAX you may want to trigger an 'input' to make sure the height is correct on init, so after loading the textarea, call $( 'textarea' ).trigger( 'input' ); – patrick Jul 02 '16 at 15:39
0

Just use <pre> </pre> with some styles like:

    pre {
        font-family: Arial, Helvetica, sans-serif;
        white-space: pre-wrap;
        word-wrap: break-word;
        font-size: 12px;
        line-height: 16px;
    }
Liber
  • 380
  • 2
  • 12
0

You can use this piece of code to compute the number of rows a textarea needs:

textarea.rows = 1;
    if (textarea.scrollHeight > textarea.clientHeight)
      textarea.rows = textarea.scrollHeight / textarea.clientHeight;

Compute it on input and window:resize events to get auto-resize effect. Example in Angular:

Template code:

<textarea rows="1" reAutoWrap></textarea>

auto-wrap.directive.ts

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: 'textarea[reAutoWrap]',
})
export class AutoWrapDirective {

  private readonly textarea: HTMLTextAreaElement;

  constructor(el: ElementRef) {
    this.textarea = el.nativeElement;
  }

  @HostListener('input') onInput() {
    this.resize();
  }

  @HostListener('window:resize') onChange() {
    this.resize();
  }

  private resize() {
    this.textarea.rows = 1;
    if (this.textarea.scrollHeight > this.textarea.clientHeight)
      this.textarea.rows = this.textarea.scrollHeight / this.textarea.clientHeight;
  }

}
André
  • 12,089
  • 5
  • 42
  • 42
0

Native Javascript solution without flickering in Firefox and faster than method withclientHeight...

1) Add div.textarea selector to all your selectors containing textarea. Do not forget to add box-sizing: border-box;

2) Include this script:

function resizeAll()
{
   var textarea=document.querySelectorAll('textarea');
   for(var i=textarea.length-1; i>=0; i--)
      resize(textarea[i]);
}

function resize(textarea)
{
   var div = document.createElement("div");
   div.setAttribute("class","textarea");
   div.innerText=textarea.value+"\r\n";
   div.setAttribute("style","width:"+textarea.offsetWidth+'px;display:block;height:auto;left:0px;top:0px;position:fixed;z-index:-200;visibility:hidden;word-wrap:break-word;overflow:hidden;');
   textarea.form.appendChild(div);
   var h=div.offsetHeight;
   div.parentNode.removeChild(div);
   textarea.style.height=h+'px';
}

function resizeOnInput(e)
{
   var textarea=document.querySelectorAll('textarea');
   for(var i=textarea.length-1; i>=0; i--)
      textarea[i].addEventListener("input",function(e){resize(e.target); return false;},false);
}

window.addEventListener("resize",function(){resizeAll();}, false);
window.addEventListener("load",function(){resizeAll();}, false);
resizeOnInput();

Tested on IE11, Firefox and Chrome.

This solution creates div similar to your textarea including internal text and measures height.

18C
  • 1,774
  • 9
  • 16
0

I created a small (7kb) custom element that deals with all of this resizing logic for you.

It works everywhere, because it's implemented as a custom element. Including: Virtual DOMs (React, Elm, etc.), server-side rendered stuff like PHP and plain boring HTML files.

Apart from listening for the input event, it also has a timer that fires every 100ms to make sure things are still working in case the text content changes by some other means.

Here's how it works:

// At the top of one of your Javascript files
import "autoheight-textarea";

or include as a script tag

<script src="//cdn.jsdelivr.net/npm/autoheight-textarea@1.0.1/dist/main.min.js"></script>

then just wrap your textarea elements like so

HTML File

<autoheight-textarea>
  <textarea rows="4" placeholder="Type something"></textarea>
<autoheight-textarea>

React.js Component

const MyComponent = () => {
  return (
    <autoheight-textarea>
      <textarea rows={4} placeholder="Type something..." />
    </autoheight-textarea>
  );
}

Here's a basic demo on Codesandbox: https://codesandbox.io/s/unruffled-http-2vm4c

And you can grab the package here: https://www.npmjs.com/package/autoheight-textarea

If you're just curious to see the resizing logic, you can take a look at this function: https://github.com/Ahrengot/autoheight-textarea/blob/master/src/index.ts#L74-L85

Ahrengot
  • 1,536
  • 1
  • 17
  • 28
0

This is a jQuery version of Moussawi7's answer.

$(function() {
  $("textarea.auto-grow").on("input", function() {
    var element = $(this)[0];
    element.style.height = "5px";
    element.style.height = (element.scrollHeight) + "px";
  });
})
textarea {
  resize: none;
  overflow: auto;
  width: 100%;
  min-height: 50px;
  max-height: 150px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea class="auto-grow"></textarea>
bafsar
  • 972
  • 2
  • 14
  • 15
0

I know I'm late to this party but the simplest solution I've come across is to split your text area content on new line characters and update the rows for the textarea element.

<textarea id="my-text-area"></textarea>

<script>
  $(function() {
    const txtArea = $('#my-text-area')
    const val = txtArea.val()
    const rowLength = val.split('\n')
    txtArea.attr('rows', rowLength)
  })
</script>
0

This is a row based approach that lets you set a maximum number of rows for the textarea after which the textarea will show scrollbar. Besides adjusting its height in the form of rows attribute, this will also auto-expand its width while being typed or doing things like cut and paste.

If the textarea doesn't have any content but a placeholder, it will adjust its width and height as per the placeholder text.

One drawback to this version is that it will continue increasing its width infinitely based on the text width. So, you will want to set a max-width value to the textarea. A simple max-width: 100%; will also do the trick. This width expanding feature is primarily based on input fields of type="text". You can read more about it in this answer.

const textarea = document.querySelector('textarea');

setTextareaWidthHeight(textarea);
textarea.addEventListener('input', setTextareaWidthHeight.bind(this, textarea));

function getInputWidth(element) {
    const text = element.value || element.placeholder;
    const elementStyle = window.getComputedStyle(element);
    const fontProperty = elementStyle.font;
    const horizontalBorder = parseFloat(elementStyle.borderLeftWidth) + parseFloat(elementStyle.borderRightWidth);
    const horizontalPadding = parseFloat(elementStyle.paddingLeft) + parseFloat(elementStyle.paddingRight);

    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    context.font = fontProperty;
    const textWidth = context.measureText(text).width;

    const totalWidth = horizontalBorder + horizontalPadding + textWidth + "px";
    return totalWidth;
}

function setTextareaWidthHeight(element) {
    // store minimum and maximum rows attribute value that should be imposed
    const minRows = 1;
    const maxRows = 5;

    // store initial inline overflow property value in a variable for later reverting to original condition
    const initialInlineOverflowY = element.style.overflowY;

    // change overflow-y property value to hidden to overcome inconsistent width differences caused by any scrollbar width
    element.style.overflowY = 'hidden';

    const totalWidth = getInputWidth(element);
    element.style.width = totalWidth;

    let rows = minRows;
    element.setAttribute("rows", rows);

    while (rows <= maxRows && element.scrollHeight !== element.clientHeight) {
        element.setAttribute("rows", rows);
        rows++;
    }

    // change overflow to its original condition
    if (initialInlineOverflowY) {
        element.style.overflowY = initialInlineOverflowY;
    } else {
        element.style.removeProperty("overflow-y");
    }
}
textarea {
    max-width: 100%;
}
<textarea placeholder="Lorem ipsum dolor sit amet"></textarea>
arafatgazi
  • 103
  • 1
  • 4
0

There is a definitive answer here:

backspace increases value of scrollheight

It uses modern ES6 syntax and solves the issues around accurate resizing when content is added or removed. It solves the issue around the constantly-changing scrollheight values.

Mark Tyers
  • 2,647
  • 4
  • 24
  • 47
0

This is a simple way to do using React.

...
const textareaRef = useRef();

const handleChange = (e) => {
  textareaRef.current.style.height = "auto";
  textareaRef.current.style.height = textareaRef.current.scrollHeight + "px";
};

return <textarea ref={textareaRef} onChange={handleChange} />;
Ricardo Canelas
  • 1,952
  • 23
  • 19
-1

I'm able to set the TextArea size in IE9 and Chrome with the following jQuery function. It binds to the textarea objects from the selector defined in the $(document).ready() function.

function autoResize(obj, size) {
    obj.keyup(function () {
        if ($(this).val().length > size-1) {
            $(this).val( function() {
                $(this).height(function() {
                    return this.scrollHeight + 13;
                });
                alert('The maximum comment length is '+size+' characters.');
                return $(this).val().substring(0, size-1);
            });
        }
        $(this).height(function() {
            if  ($(this).val() == '') {
                return 15;
            } else {
                $(this).height(15);
                return ($(this).attr('scrollHeight')-2);
            }
        });
    }).keyup();
}

In my $(document).ready() function I have the following call for all of my textarea calls on this page.

$('textarea').each( function() {
        autoResize($(this), 250);
});

Where 250 is the character limit on my text area. This will grow as large as the text size will allow (based on your character count and font size). It will also shrink your text area appropriately when you remove characters from your textarea or if the user pastes too much text initially.

Matthew
  • 179
  • 1
  • 3
  • 12
-1

I have tested script in common browsers, and it failed in Chrome and Safari. It is because of constantly updatable scrollHeight variable.

I have applied DisgruntledGoat script using jQuery and added chrome fix

function fitToContent(/* JQuery */text, /* Number */maxHeight) {
    var adjustedHeight = text.height();
    var relative_error = parseInt(text.attr('relative_error'));
    if (!maxHeight || maxHeight > adjustedHeight) {
        adjustedHeight = Math.max(text[0].scrollHeight, adjustedHeight);
        if (maxHeight)
            adjustedHeight = Math.min(maxHeight, adjustedHeight);
        if ((adjustedHeight - relative_error) > text.height()) {
            text.css('height', (adjustedHeight - relative_error) + "px");
            // chrome fix
            if (text[0].scrollHeight != adjustedHeight) {
                var relative = text[0].scrollHeight - adjustedHeight;
                if (relative_error != relative) {
                    text.attr('relative_error', relative + relative_error);
                }
            }
        }
    }
}

function autoResizeText(/* Number */maxHeight) {
    var resize = function() {
        fitToContent($(this), maxHeight);
    };
    $("textarea").attr('relative_error', 0);
    $("textarea").each(resize);
    $("textarea").keyup(resize).keydown(resize);
}
-1

You can use this code:

Coffescript:

jQuery.fn.extend autoHeightTextarea: ->
  autoHeightTextarea_ = (element) ->
    jQuery(element).css(
      'height': 'auto'
      'overflow-y': 'hidden').height element.scrollHeight

  @each ->
    autoHeightTextarea_(@).on 'input', ->
      autoHeightTextarea_ @

$('textarea_class_or_id`').autoHeightTextarea()

Javascript

jQuery.fn.extend({
  autoHeightTextarea: function() {
    var autoHeightTextarea_;
    autoHeightTextarea_ = function(element) {
      return jQuery(element).css({
        'height': 'auto',
        'overflow-y': 'hidden'
      }).height(element.scrollHeight);
    };
    return this.each(function() {
      return autoHeightTextarea_(this).on('input', function() {
        return autoHeightTextarea_(this);
      });
    });
  }
});

$('textarea_class_or_id`').autoHeightTextarea();
Darex1991
  • 663
  • 1
  • 6
  • 23
-1
$('textarea').bind('keyup change', function() {
    var $this = $(this), $offset = this.offsetHeight;
    $offset > $this.height() && $offset < 300 ?
        $this.css('height ', $offset)
            .attr('rows', $this.val().split('\n').length)
            .css({'height' : $this.attr('scrollHeight'),'overflow' : 'hidden'}) :
        $this.css('overflow','auto');
});
Algorithm
  • 329
  • 6
  • 15