Reputation: 72510
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 )
};
}
Upvotes: 544
Views: 733665
Reputation: 25341
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: 0 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.
Upvotes: 12
Reputation: 60001
As of 2024, there's a new answer that doesn't need hacks or 3rd party libraries: the field-sizing
attribute being added to text inputs as part of web standards. Details in the field-sizing standards proposal explainer.
Update April 2024: for more info, see CSS field-sizing on Chrome dev docs and field-sizing on MDN
With the new field-sizing style, you can instruct <textarea>
or <input type="text">
to size themselves based on content:
textarea {
field-sizing: content;
}
Chromium-based browsers are expected to ship this feature in Chrome 123, expected to arrive March 2024 timeframe.
Here's a quick code snippet to see if your browser supports this new standard:
// Let the user know whether field-sizing is supported.
const textArea = document.querySelector("textarea");
const resultSpan = document.querySelector("span");
resultSpan.innerText = ("fieldSizing" in textArea.style) ? '✅' : '❌'
textarea {
field-sizing: content;
min-width: 200px; /* optional: a minimum width, otherwise textarea can become very small with no content */
max-height: 10lh; /* optional: after 10 lines, give a scrollbar */
}
<textarea>This textarea automatically grows based on its content</textarea>
<h1>Your browser's support for field sizing: <span></span></h1>
Upvotes: 31
Reputation: 34177
The following code will work:
This option requires jQuery and has been tested and is working with 1.7.2 - 3.7.1
Simple (Add this jQuery code to your master script file and forget about it.)
$("textarea").each(function () {
this.style.height = this.scrollHeight + "px";
this.style.overflowY = "hidden";
}).on("input", function () {
this.style.height = "auto";
this.style.height = this.scrollHeight + "px";
});
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.7.1.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>
Simple (Add this JavaScript to your master script file and forget about it.)
document.querySelectorAll("textarea").forEach(function(textarea) {
textarea.style.height = textarea.scrollHeight + "px";
textarea.style.overflowY = "hidden";
textarea.addEventListener("input", function() {
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>
Useful if you want to apply further chaining to the textareas, you want to be auto-sized.
jQuery.fn.extend({
autoHeight: function () {
function setAutoHeight(element) {
jQuery(element).css({ height: 'auto', overflowY: 'hidden' });
jQuery(element).height(element.scrollHeight);
}
return this.each(function() {
setAutoHeight(this);
jQuery(this).on("input", () => setAutoHeight(this));
});
}
});
Invoke with $("textarea").autoHeight()
When injecting content into a textarea via JavaScript, append the following line of code to invoke the resize function.
jQuery
$("textarea").trigger("input");
Pure JavaScript
document.querySelectorAll("textarea").forEach(t => t.dispatchEvent(new Event('input', { bubbles: true, cancelable: true })));
To fix the initial height of the textarea you will need to add another condition:
const txHeight = 16; // Preset initial height in pixels
const tx = document.getElementsByTagName("textarea");
for (let i = 0; i < tx.length; i++) {
if (tx[i].value === '') {
tx[i].style.height = txHeight + "px";
} else {
tx[i].style.height = tx[i].scrollHeight + "px";
}
tx[i].style.overflowY = "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>
Upvotes: 678
Reputation: 136
In modern browsers, no JS is needed anymore. Currently only works in Chrome and Edge
textarea {
field-sizing: content;
}
Upvotes: 1
Reputation: 13600
You don't need overly complicated calculations or style manipulations. Here is a clear and concise way to do it:
oninput
event listener to the textarea element.rows
attribute via currentTarget
const handleInput = (event) => {
const lines = event.currentTarget.value.split(/\r\n|\r|\n/).length
event.currentTarget.setAttribute('rows', lines.toString());
}
el.addEventListener('input', handleInput);
You can control the initial height using the very same way:
const content = 'Some Content';
const lines = content.split(/\r\n|\r|\n/).length
el.setAttribute('rows', lines.toString());
el.innerHTML = content;
Here is an example demonstrating it:
const handleInput = (event) => {
const lines = event.currentTarget.value.split(/\r\n|\r|\n/).length
event.currentTarget.setAttribute('rows', lines.toString());
}
const el = document.createElement('textarea');
el.addEventListener('input', handleInput);
const content = 'Some Content';
const lines = content.split(/\r\n|\r|\n/).length
el.setAttribute('rows', lines.toString());
el.innerHTML = content;
document.body.appendChild(el);
Upvotes: -2
Reputation: 2876
Pure CSS solution:
textarea {
form-sizing: content;
}
Warning: This is not supported in most browsers (e.g. firefox and safari), but support will likely increase in the coming months. I would wait until it is more widely available to use it.
Upvotes: 3
Reputation: 45
for Vue2 directives:
directives: {
autoResize: {
// autoResize
inserted: (el) => {
function OnInput() {
el.style.height = 'auto';
el.style.height = (this.scrollHeight) + "px";
}
el.setAttribute("style", "height:" + (el.scrollHeight) + "px;overflow-y:hidden;");
el.addEventListener("input", OnInput, false);
}
}
}
Upvotes: 0
Reputation: 18184
If you are using react I would recommend TextareaAutosize
library.
It's nothing but simple textarea under the hood but frees you of the hassles of styling issues and if you are already using the styling for textarea
then you dont need to add any additional styling either.
on terminal:
npm install react-textarea-autosize
in code:
import TextareaAutosize from 'react-textarea-autosize';
// If you use CommonJS syntax:
// var TextareaAutosize = require('react-textarea-autosize').default;
React.renderComponent(
<div>
<TextareaAutosize />
</div>,
document.getElementById('element'),
);
Upvotes: 0
Reputation: 10044
<!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>
Upvotes: 33
Reputation: 25978
If you don't want to shift around things while the user is typing, you could just resize it when the user leaves (blur), or maybe other appropriate events. That's what did it for me:
shrinkOnBlur(event) {
setTimeout(function () {
event.target.rows = 1;
while (event.target.scrollHeight > event.target.clientHeight) {
event.target.rows++;
}
}, 100);
}
Possibly you can add some space on focus (which is removed again on blur).
Upvotes: 0
Reputation: 1164
I think this answer is almost more complete and correct because it uses the rows
property of the textarea:
function addLiveAutoHeight(id, maxHeight=10) {
const tag = document.getElementById(id);
if(!Number.isInteger(maxHeight)){
tag.style.maxHeight = maxHeight;
maxHeight = 999999999;
}
tag.rows = Math.min(maxHeight,tag.value.split("\n").length);
document.getElementById(id).oninput = function () {
this.rows = Math.min(maxHeight,this.value.split("\n").length);
}
}
and to use:
addLiveAutoHeight("MY_TEXTAREA_ID");
Enjoy...
Upvotes: 0
Reputation: 278
if you want to use only css
you can also create your element like this:
function contentTransfer(){
document.getElementsByClassName("container")[0].innerHTML = document.getElementById("myTextArea").innerHTML;
}
.custom_textArea{
border: 1px solid lightGray;
padding: 7px;
border-radius: 7px;
outline: none;
}
.custom_textArea:focus{
border: 1px solid darkGray;
}
<p id="myTextArea" class="custom_textArea" contenteditable="true">my text</p>
<button onclick="contentTransfer()">get content</button>
<p class="container"></p>
Upvotes: 1
Reputation: 2035
I wanna use this
window.auto_grow = async element => {
element.style.height = "5px";
element.style.height = (element.scrollHeight) + "px";
}
and call it
<textarea oninput="window.auto_grow(this)" onfocus="window.auto_grow(this)"></textarea>
Upvotes: 1
Reputation: 1108
This is a mixed JS/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";
});
});
And this one is Mystral's comment. It's more like jquery code.
$(function() {
$("textarea.auto-grow").on("input", function() {
$(this).css("height", "5px")
.css("height", $(this).prop("scrollHeight") + "px");
});
});
CSS:
<style>
textarea {
resize: none;
overflow: auto;
width: 100%;
min-height: 50px;
max-height: 150px;
}
</style>
Html:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea class="auto-grow"></textarea>
Upvotes: 0
Reputation: 1000
The following also takes care of the bottom padding as well as window resize. Like this, it's a near perfect solution for me. Big thanks to him.
let textareas = document.getElementsByClassName("auto-resize-textarea");
// Loop through textareas and add event listeners as well as other needed css attributes
for (const textarea of textareas) {
// Initially set height as otherwise the textarea is not high enough on load
textarea.style.height = textarea.scrollHeight.toString();
// Hide scrollbar
textarea.style.overflowY = 'hidden';
// Call resize function with "this" context once during initialisation as it's too high otherwise
resizeTextarea.call(textarea);
// Add event listener to resize textarea on input
textarea.addEventListener('input', resizeTextarea, false);
// Also resize textarea on window resize event binding textarea to be "this"
window.addEventListener('resize', resizeTextarea.bind(textarea), false);
}
function resizeTextarea() {
// Textareas have default 2px padding and if not set it returns 0px
let padding = window.getComputedStyle(this).getPropertyValue('padding-bottom');
// getPropertyValue('padding-bottom') returns "px" at the end it needs to be removed to be added to scrollHeight
padding = parseInt(padding.replace('px',''));
this.style.height = "auto";
this.style.height = (this.scrollHeight) + "px";
}
textarea {
width:40%;
padding:20px 25px;
border-radius: 20px;
}
<textarea class="auto-resize-textarea">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</textarea>
<textarea placeholder="Type, paste, cut text here..." class="auto-resize-textarea"></textarea>
Note: there is a weird issue with jsfiddle where the textarea is too high and there is too much space at the bottom, but copying and pasting this exact code to an empty HTML-file works perfectly.
There is a small issue though when a scrollbar appears on the page and the textarea shrinks and wraps the text and creates a new line. The above function does not take that into account and I made a question, but no-one seems to know a fix. If you have suggestions to resolve the issue, I would be very glad.
Upvotes: 3
Reputation: 2460
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} />;
Upvotes: 5
Reputation: 3047
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.
Upvotes: 19
Reputation: 2290
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>
Upvotes: 2
Reputation: 5250
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;
}
Upvotes: 0
Reputation: 197
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>
Upvotes: 5
Reputation: 532
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>
Upvotes: 7
Reputation: 471
Found an one liner from here;
<textarea name="text" oninput="this.style.height = ''; this.style.height = this.scrollHeight +'px'"></textarea>
Upvotes: 43
Reputation: 93
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);
});
Upvotes: 2
Reputation: 1895
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)
Upvotes: 3
Reputation: 4721
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';
}
}
Upvotes: 3
Reputation: 614
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.
Upvotes: 0
Reputation: 5282
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>
Upvotes: 4
Reputation: 382492
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
Upvotes: 13
Reputation: 3229
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();
Upvotes: 2
Reputation: 885
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();
}
Upvotes: 2