<textarea> elements don’t change size based on the content they contain. In fact, there isn’t any simple HTML or CSS way to make them do that. Kinda funny, as that seems like a reasonable use-case. But of course, there are ways, my friend. There are always ways.
I was thinking about this after Remy Sharp blogged about it recently in the context of inline
Non-input elements expand naturally
It’s weird to me that there is no way to force an input element to mimic this behavior, but alas.
We can make any element editable and input-like with the
<span class="input" role="textbox" contenteditable> 99 </span>
<span> will naturally grow to be the width it needs to be for the content it contains. If it was a
<div> or any other element that is block-level, it would also expand vertically as needed.
But are non-input elements accessible?
I’m not entirely sure. Notice I put
role="textbox" on the element. That’s just a best-guess based on some docs.
Even if that’s helpful…
- What about the fact that forms can be submitted with the Enter key?
- What about the idea that form data is often serialized and sent along, while the code that’s doing it probably isn’t looking for a span?
- Does it actually read the same as an input in a screen reader?
- What other things¹ do inputs naturally do that I’m not thinking of?
As attracted as I am to the idea that we can get auto-resizing for free from the browser by using non-input elements, I’m also a little worried about (my) unknown usability and accessibility risk.
Resizing actual input elements
So let’s say we stick with
<textarea>. Can we make them resize-able even though it’s not particularly natural?
For textareas, one classic technique is to count the number of line-breaks, use that to set the height, then multiply it by the line-height. That works great for preformatted text, like code, but not at all for long-form paragraph-like content.
Here are all these ideas combined.
data-* attribute on the element equal to the value of the input. The input is set within a CSS grid, where that grid is a pseudo-element that uses that
data-* attribute as its content. That content is what stretches the grid to the appropriate size based on the input value.
I absolutely know that you fellow web nerds have solved this six ways to Sunday. Let’s see ’em in the comments.
- Eric Bailey hit me up with a few thoughts off the top of his head: (1) There’s no accessible name. (2) It probably won’t work with voice control. (3) It will get ignored in High Contrast Mode.
That one-liner with CSS grid by Shaw is genius! Does it have any drawbacks or side effects? I guess we wouldn’t know until it was in a real layout, but it seems fine to me.
FWIW, I created a solution like 8 years ago (Chris, you even commented on it!) which uses a clone div element, from which I ‘borrow’ the size for the textarea. I’ve recently updated it to not use jQuery and use more modern JS (though it still has some oldIE stuff in there that you can remove). CodePen here.
Using ::before or ::after might be a problem. Depending on your browser & screen reader combo, the generated content will get read out loud. With a separate html element, you can set aria-hidden to prevent that, like in the first example.
The SPAN method is very useful and I’m using it but there is a problem. Copy & Paste pastes a span into the block. I’ll look into editing via onkeydown or something possibly but it’s strange because the span doesn’t simply paste into an editor, only the span that’s mimicking the textarea. If anyone has a solution lemme know.
Used a method of setting
overflow-y: hidden;on the textarea then use eventHandler watching on the
inputevent, Height style is then updated with the scrollHeight value.
Don’t forget to set the height on page load/dom insert too.
I use a modified version I cobbled together from multiple sources online, with only minor tweaks.
It’s a bit clumsy because I didn’t make it check for dynamically created elements, so I added “mousedown” event.
Also, this is the jQuery version for brevity.
If anyone has any improvements, I’d like to know. I don’t like hacks at all, so I really not inclined to use the examples from the article.
Maybe this calls for a simple browser feature? Or an extension to CSS?
Felt compelled to make a codepen for this. (I tweaked it a bit from the comment above)
This is pretty good, although not flawless. The “expand on click/focus” example grew much taller than it needed to be (at least on my Chrome/Windows 10 browser). The “expand as you type” example is better behaved although it sometimes has a blank line at the end and sometimes not (seems to depend on the text being entered). But overall I like your solution better than mine.
My solution was to create a hidden DIV with the same width, borders, margins, padding, font size, line height, etc. as the TEXTBOX, duplicate the content from the TEXTBOX into the DIV, and then measure the height of the DIV and apply that height to the TEXTBOX.
Your solution has the benefit of not needing the auxiliary DIV.
Ok, using scroll height is kind of genius. I’m going to steal this concept for sure!
Ok, I decided to use this method in my Vue project. I’m also dynamically setting/removing overflow to prevent scroll-bar flashing by watching the computed value of the max-height of the textarea. A key thing here is to set rows=”1″ which seems to be set to 2 by default so when you delete all of your lines you can go back to your starting height. This more or less ticks all of the boxes including being fully responsive.
See the Pen Auto Height Adjusting Textarea by Ian
(@iancwoodward) on CodePen.
Nice ideas here, all with various pros and contras. I think it’s always best to choose the right approach for a project, there’s almost never one single solution to problems.
We’ve build native textarea resizing in a ECMAScript Class and wrote about it for Colloq last year. Also relatively simply and effective and using a modern approach.
A couple of years ago, I had a need to let a <DIV> that was being used for a popup message, expand when the output text for the popup message would expand beyond the borders of the message box.
I couldn’t remember how I did that, so this morning I was searching the Internet for the same solution, when I came upon your post. Later, I found my old code and modified it to meet my needs today, but then I expanded it and created a DEMO which expands INPUT and TEXTAREA HTML elements when its content expands, that I could post back here. I pushed the DEMO up to a public GitHub repository.
You can find the demo here:
Hope this helps someone.
For the solution, is there a CSS solution that the pasted text won’t be styled? that it will be pasted as plain text?
<span>method, is there a CSS solution that the pasted text won’t be styled? that it will be pasted as plain text?
Here is my web-component version of the clever data-* solution:
Why not just use .scrollWidth?
only for input:
<input type="text" size="1" oninput="this.setAttribute('size',this.value.length||=1)">
Loved the solution by Shaw was great! Worked very well in my app. thanks!
in microsoft teams we have textarea right like that features in need to build in angular like same features