How to auto resize the textarea to fit the content?
Asked Answered
A

7

13

I'm trying to auto resize the textarea so it fits the content in it but I'm facing a stuttering issue after I click enter to proceed to the next line. How can I fix this?

This is what I'm trying to do, see the image below. enter image description here

Please see this link for the StackBlitz example

CODE

this.form.valueChanges.subscribe(() => {
   const textarea = this.myDiv.nativeElement;
   textarea.addEventListener('keydown', function() {
       setTimeout(() => {
           this.style.cssText = 'height:auto; padding:0';
           this.style.cssText = 'height:' + this.scrollHeight + 'px';
       }, 0);
   });
});
Aurelie answered 24/9, 2019 at 4:16 Comment(12)
sorry unclear on what you are asking.Tushy
@joyBlanks. Enter text, then click enter, it proceeds to next line, then enter text again, it proceeds to next line. But do you the stuttering(the cursor suddenly goes up then down fastly)Aurelie
works smoothly for me Mac safari/chrome latestTushy
ok I see in chrome its jumpy. on safari works perfectly fineTushy
@joyBlanks. yes thats the problem, on chrome its jumpy. Althought not a big deal but i want to fix itAurelie
this is a very old trick hack. We need to come up with something better. Do you have to pre-populate data on this text-area or it is always going to start with empty?Tushy
@joyBlanks. It always start with emptyAurelie
I noticed this happens whenever <textarea> element must be resized. Having a min-height would prevent this issue. You can see it live on StackBlitz. But when cursor reaches last line, entering more text would result in resizing and the same issue repeats.Keikokeil
@Nikhil. Actually i'm trying to follow pinterest. pinterest.ph/pin-builder. I'm trying to create a behavior like thatAurelie
@Aurelie - You could see that they are changing textarea's height dynamically. You can try something like that or use min-height and max-height to avoid this issue altogether. You will get a scrollbar in the textarea instead of an ever-expanding element. See it live on StackBlitz.Keikokeil
@Aurelie It seems my solution will only work if you separate out lines by line breaks if you have a single long word it cant work eg wwwww...wwww very long spanning 2 3 rows. I ll post a solutionTushy
@Aurelie sadly there is no straight forward way to achieve this, So used a html element proxy to achieve the solution. Its hacky like the solution you started with but a little different as you no longer have jumpy text area. But my solution now works for long lines and line breaks everything.Tushy
B
12

addEventListener here is redundant since valueChanges already notifies you when the field changes. Instead, update the height using the ViewChild reference myDiv.

this.myForm.valueChanges.subscribe(value => {
    this.myDiv.nativeElement.style.height = 'auto';
    this.myDiv.nativeElement.style.height = `${this.myDiv.nativeElement.scrollHeight}px`;
});

Then add overflow: hidden to your css so the scrollbar doesn't show.

textarea {
    resize: horizontal;
    overflow: hidden;
}

You can keep the resize: horizontal; but it is no longer required since the textarea will resize automatically anyway.

Here is a working example on StackBlitz.

Beryllium answered 24/9, 2019 at 5:26 Comment(5)
Great man, but after resizing the textarea it's not working. please checkPungy
@RanjithVaradan - Do you even need to resize vertically here? From a user's perspective, if your textarea just fits the content, the vertical scrolling should not be needed since they can see everything they have typed. Why not just disable vertical resizing?Beryllium
@RanjithVaradan - Thanks :) I have updated my answer regarding the resizing issue as well.Beryllium
@nash11. Hi thanks for your work. However, there is a very small problem left. When i try to enter text for long lines it should not hide the text above. Pls see my image above. Pls also see my forked link here. stackblitz.com/edit/…Aurelie
@nash11. Thanks you got itAurelie
E
5

FOR THOSE THAT HAVE THE ANGULAR MATERIAL CDK INSTALLED IN THEIR APPLICATION:

This behavior is covered in the official Angular Material docs here.

<mat-form-field [style.fontSize]="fontSize.value">
  <mat-label>Autosize textarea</mat-label>
  <textarea matInput
            cdkTextareaAutosize
            #autosize="cdkTextareaAutosize"
            cdkAutosizeMinRows="1"
            cdkAutosizeMaxRows="5"></textarea>
</mat-form-field>

If you would prefer not to install the CDK, you can look at and perhaps copy the directive from the CDK into your application. The Github for the autosize directive is here.

Exultant answered 15/12, 2020 at 12:49 Comment(5)
This is a much better approach than the accepted one. Thanks!Overripe
This worked perfectly well and less complicated too.Bouffe
You just forgot to specify that everyone should rewrite their code to use angular-material for your solution.Maritsa
@Maritsa Happy now?Exultant
This is better, then previous statement. But why you use CAPS?Maritsa
F
3
<textarea rows="15" id="code" disabled placeholder="Description"></textarea>

rows="15" to set min heigth to 15 rows

el = document.getElementById("code");
el.style.height = "auto";
// code to populate textarea
el.style.height = (5 + el.scrollHeight) + "px";

this helped me to set textarea to height as per content populated in it.

Fur answered 22/7, 2022 at 12:15 Comment(0)
T
1

What you are trying to achieve is a very old trick. I have used it myself but trying a different approach.

It makes more sense why the text area is jumpy coz every keystroke you were making the height = 0 to calculate scroll height so that you can assign a new height.

I calculated the fontSize or lineHeight and calculated number of lines and the initial height to adjust based on that. So on every keystroke you are just assigning height w/o making the text area height=0

textareaProps = null;
getHeight(element) {
  const lines = element.value.split(/\r\n|\r|\n/).length;
  if(!this.textareaProps) {
    const autoStyle = getComputedStyle(element);
    const lineHeight = parseInt(autoStyle.lineHeight);
    const adjust = parseInt(autoStyle.height) - lineHeight;
    this.textareaProps = {adjust, lineHeight}
  }
  const { adjust, lineHeight } = this.textareaProps;
  const height = lines * lineHeight + adjust;
  return height + 'px';
}

You now need to call this method to get height and pass the textarea element as arg.

element.style.cssText = 'height:' + getHeight(element) ;

Edit 2

Sadly the above solution will only work if there are line breaks by user. When you enter a huge line text area wraps it but it doesn't increase the height. So intruducing a proxy html element which will have the text as same as text area value and will provide a height that we can assign to our text area.

textareaProps = null;
getHeight(element) {
  if(!this.textareaProps) {
    const proxy = document.createElement('div');
    const {padding, width, fontSize, height, lineHeight} = getComputedStyle(element);
    const css = [
      'position:absolute',
      'visibility: hidden',
      'pointer-events:none',
      `width: ${width}`,
      `padding:${padding}`,
      `min-height: ${height}`,
      `font-size:${fontSize}`,
      `line-height:${lineHeight}`,
    ].join(';');
    proxy.style.cssText=css;
    this.textareaProps = {
      proxy: document.body.appendChild(proxy), 
      adjust: (parseInt(fontSize))
    };
  }
  const { proxy, adjust} = this.textareaProps;
  proxy.innerText = element.value + '.';
  return (proxy.offsetHeight + adjust) + 'px';
}

Updated StackBlitz https://stackblitz.com/edit/next-line-view-child-ssnp4q

Tushy answered 24/9, 2019 at 5:21 Comment(4)
Pls see again my edited image. I'm trying to achieve that. Its found on pinterest. pinterest.ph/pin-builderAurelie
Ya please check my updated edit 2 this should work for you.Tushy
@Joseph: Please check the updated Stackblitz: stackblitz.com/edit/next-line-view-child-ssnp4q. I think it should work for youTushy
Thanks anyway, Nash got it first. Anyway i already upvoted you. Great jobAurelie
S
1

If you dont want a plugin there is a very simple solution

$(document).ready(function() {
     $('textarea').on('keyup keypress', function() {
        $(this).height(0);
        $(this).height(this.scrollHeight);
    }); 
    
    $("textarea").each(function(textarea) {
         $(this).height(0);
        $(this).height(this.scrollHeight);
    });

});
Swayback answered 8/6, 2022 at 9:59 Comment(1)
The question is about JS, not jQuery.Surculose
M
1

There is experimental CSS support for field-sizing: content

textarea {
    field-sizing: content
}

As of June 2024, it's supported in Chrome and Edge, but not in Firefox or Safari.

Mcdaniel answered 3/7 at 15:10 Comment(0)
S
0

**Inside of textarea add following lines this will increase and decrease rows **

rows={(value.match(/\n/g) || []).length + 1}

style={{ height: "auto", resize: "horizontal", overflow: "hidden", }}


in here value is a state variable also update it using onchange this works checked.

Schlemiel answered 5/4 at 9:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.