93

I can set initial text input size in css, like so:

width: 50px;

But I would like it to grow when I type until it reaches for example 200px. Can this be done in straight css, html, preferably without javascript?

Do post your js/jquery solutions too of course, but if this is doable without them - that be great.

my try here:

http://jsfiddle.net/jszjz/2/

Stann
  • 13,518
  • 19
  • 65
  • 73

16 Answers16

113

Here is an example with only CSS and Content Editable:

jsFiddle Example

CSS

span 
{
    border: solid 1px black;
}
div 
{
    max-width: 200px;   
}

HTML

<div>
    <span contenteditable="true">sdfsd</span>
</div>

Important note regarding contenteditable

Making an HTML element contenteditable lets users paste copied HTML elements inside of this element. This may not be ideal for your use case, so keep that in mind when choosing to use it.

diogo.silva
  • 448
  • 4
  • 13
Joe
  • 80,724
  • 18
  • 127
  • 145
  • this is pretty nice. Do you know if there are compatibility issues with contenteditable? – Stann Aug 24 '11 at 00:02
  • 4
    YAY. I've just tried it in IE6-9, latest Opera, Firefox and Chrome - and OMG - it worked everywhere! – Stann Aug 24 '11 at 00:05
  • 5
    For me in Chrome this extends the element down a row when it reaches max-width. It also won't work if the input is intended to be part of a form. – Paul Aug 24 '11 at 00:07
  • @PaulPRO: no. this is for a tagging plugin that will sort of imitate tagging addition on this site. I'll try it tonite and see what'll come out. – Stann Aug 24 '11 at 00:12
  • Wow, this is actually the simpliest and fastest one. Good job! – Pantelis Aug 24 '11 at 00:22
  • @Paulpro I adjusted the answer to have it stop wrapping to a new line. Be aware, I could not adjust the jsFiddle, just the answer here. – dallin Sep 03 '13 at 17:40
  • not an input text field. `contenteditable` is surprisingly compatible but its usability varies across platforms (e.g. the behaviour of arrow keys at the edge of the box can be strange on Firefox). – Oliver Moran Nov 11 '13 at 08:33
  • 7
    For a multi-line input field (custom line breaks), replace `span` by `div`. Note that `word-wrap: break-word` will be necessary or words longer than `max-width` will overflow the div box: http://jsfiddle.net/YZPmC/157/ – CodeManX Jan 06 '14 at 10:33
  • 7
    Make sure that you scrub for unwanted html that could get into the contenteditable span through copy and paste. – Brian Peacock Mar 11 '14 at 22:09
  • 3
    Does this send with form? – Tomáš Zato Jan 15 '15 at 08:16
  • If you get rid of the outer div and just set a min-width and/or max-width on the span, and set the span to display: inline-block, it resizes and works amazingly well for my own use-case. – lastmjs Mar 24 '16 at 19:02
  • Is this a best practice? I know the frontend works correctly, but as @TomášZato says, does it send a form? Is it usable with frameworks and how does it cork on mobile devices? – nclsvh Nov 03 '16 at 11:00
  • 2
    not using an actual `input[type=text]` introduces so many issues/so much complexity – oldboy Oct 06 '19 at 22:58
  • 1
    **Beware**. Scrubbing (i.e. replacing the innerHTML with a filtered version) messes up the cursor position (tested on Firefox). This results in new typed characters added to the **front** of the text instead of at the back or at the cursor position. (Related to @BrianPeacock's comment.) – frIT Feb 07 '20 at 11:11
  • 1
    This is very bad practice, contenteditable is a lazy shortcut. This was designed for in-place editing of the HTML source, think in-line article editor on a news site for example. For most uses you need to use an input field and Javascript. Grow the "size" attribute by the text length every time you detect a key stroke. Trust me I would like to avoid JS if I can but this is not a good place for it. – sw1337 Sep 02 '20 at 17:41
61

I just wrote this for you, I hope you like it :) No guarantees that it's cross-browser, but I think it is :)

(function(){
    var min = 100, max = 300, pad_right = 5, input = document.getElementById('adjinput');

    input.style.width = min+'px';
    input.onkeypress = input.onkeydown = input.onkeyup = function(){
        var input = this;
        setTimeout(function(){
            var tmp = document.createElement('div');
            tmp.style.padding = '0';
            if(getComputedStyle)
                tmp.style.cssText = getComputedStyle(input, null).cssText;
            if(input.currentStyle)
                tmp.style.cssText = input.currentStyle.cssText;
            tmp.style.width = '';
            tmp.style.position = 'absolute';
            tmp.innerHTML = input.value.replace(/&/g, "&amp;")
                                       .replace(/</g, "&lt;")
                                       .replace(/>/g, "&gt;")
                                       .replace(/"/g, "&quot;")
                                       .replace(/'/g, "&#039;")
                                       .replace(/ /g, '&nbsp;');
            input.parentNode.appendChild(tmp);
            var width = tmp.clientWidth+pad_right+1;
            tmp.parentNode.removeChild(tmp);
            if(min <= width && width <= max)
                input.style.width = width+'px';
        }, 1);
    }
})();

JSFiddle

Paul
  • 139,544
  • 27
  • 275
  • 264
11

If you set the span to display: inline-block, automatic horizontal and vertical resizing works very well:

<span contenteditable="true" 
      style="display: inline-block;
             border: solid 1px black;
             min-width: 50px; 
             max-width: 200px">
</span>
Orkhan Alikhanov
  • 9,122
  • 3
  • 39
  • 60
lastmjs
  • 924
  • 3
  • 11
  • 22
6

How about programmatically modifying the size attribute on the input?

Semantically (imo), this solution is better than the accepted solution because it still uses input fields for user input but it does introduce a little bit of jQuery. Soundcloud does something similar to this for their tagging.

<input size="1" />

$('input').on('keydown', function(evt) {
    var $this = $(this),
        size = parseInt($this.attr('size'), 10),
        isValidKey = (evt.which >= 65 && evt.which <= 90) || // a-zA-Z
                     (evt.which >= 48 && evt.which <= 57) || // 0-9
                     evt.which === 32;

    if ( evt.which === 8 && size > 0 ) {
        // backspace
        $this.attr('size', size - 1);
    } else if ( isValidKey ) {
        // all other keystrokes
        $this.attr('size', size + 1);
    }
});

http://jsfiddle.net/Vu9ZT/

muffs
  • 805
  • 10
  • 14
3

A couple of things come to mind:

Use an onkeydown handler in your text field, measure the text*, and increase the text box size accordingly.

Attach a :focus css class to your text box with a larger width. Then your box will be larger when focused. That's not exactly what you're asking for, but similar.

* It's not straightforward to measure text in javascript. Check out this question for some ideas.

Community
  • 1
  • 1
Seth
  • 45,033
  • 10
  • 85
  • 120
3

From: Is there a jQuery autogrow plugin for text fields?


See a demo here: http://jsbin.com/ahaxe

The plugin:

(function($){

    $.fn.autoGrowInput = function(o) {

        o = $.extend({
            maxWidth: 1000,
            minWidth: 0,
            comfortZone: 70
        }, o);

        this.filter('input:text').each(function(){

            var minWidth = o.minWidth || $(this).width(),
                val = '',
                input = $(this),
                testSubject = $('<tester/>').css({
                    position: 'absolute',
                    top: -9999,
                    left: -9999,
                    width: 'auto',
                    fontSize: input.css('fontSize'),
                    fontFamily: input.css('fontFamily'),
                    fontWeight: input.css('fontWeight'),
                    letterSpacing: input.css('letterSpacing'),
                    whiteSpace: 'nowrap'
                }),
                check = function() {

                    if (val === (val = input.val())) {return;}

                    // Enter new content into testSubject
                    var escaped = val.replace(/&/g, '&amp;').replace(/\s/g,'&nbsp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
                    testSubject.html(escaped);

                    // Calculate new width + whether to change
                    var testerWidth = testSubject.width(),
                        newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth,
                        currentWidth = input.width(),
                        isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth)
                                             || (newWidth > minWidth && newWidth < o.maxWidth);

                    // Animate width
                    if (isValidWidthChange) {
                        input.width(newWidth);
                    }

                };

            testSubject.insertAfter(input);

            $(this).bind('keyup keydown blur update', check);

        });

        return this;

    };

})(jQuery);
Community
  • 1
  • 1
cmpolis
  • 3,051
  • 21
  • 19
2

I know this is a seriously old post - but my answer might be useful to others anyway, so here goes. I found that if my CSS style definition for the contenteditable div has a min-height of 200 instead of a height of 200 , then the div scales automatically.

b0rgBart3
  • 304
  • 1
  • 5
2

If you are just interested in growing, you can update the width to scrollWidth, whenever the content of the input element changes.

document.querySelectorAll('input[type="text"]').forEach(function(node) {
  node.onchange = node.oninput = function() {
    node.style.width = node.scrollWidth+'px';
  };
});

But this will not shrink the element.

ceving
  • 21,900
  • 13
  • 104
  • 178
2

Which approach you use, of course, depends on what your end goal is. If you want to submit the results with a form then using native form elements means you don't have to use scripting to submit. Also, if scripting is turned off then the fallback still works without the fancy grow-shrink effects. If you want to get the plain text out of a contenteditable element you can always also use scripting like node.textContent to strip out the html that the browsers insert in the user input.

This version uses native form elements with slight refinements on some of the previous posts.

It allows the content to shrink as well.

Use this in combination with CSS for better control.

<html>

<textarea></textarea>
<br>
<input type="text">


<style>

textarea {
  width: 300px;
  min-height: 100px;
}

input {
  min-width: 300px;
}


<script>

document.querySelectorAll('input[type="text"]').forEach(function(node) {
  var minWidth = parseInt(getComputedStyle(node).minWidth) || node.clientWidth;
  node.style.overflowX = 'auto'; // 'hidden'
  node.onchange = node.oninput = function() {
    node.style.width = minWidth + 'px';
    node.style.width = node.scrollWidth + 'px';
  };
});

You can use something similar with <textarea> elements

document.querySelectorAll('textarea').forEach(function(node) {
  var minHeight = parseInt(getComputedStyle(node).minHeight) || node.clientHeight;
  node.style.overflowY = 'auto'; // 'hidden'
  node.onchange = node.oninput = function() {
    node.style.height = minHeight + 'px';
    node.style.height = node.scrollHeight + 'px';
  };
});

This doesn't flicker on Chrome, results may vary on other browsers, so test.

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
2

You can do this with display: inline-grid:

const dummy = document.querySelector(".dummy");
const input = document.querySelector("input");

const update = () => dummy.innerText = input.value;
input.oninput = update;
update();
.growing-input {
  display: inline-grid;
}

.growing-input .dummy,
.growing-input input {
  grid-area: 1 / 1;
  
  /* Following properties just need to be consistent,
  to ensure the .dummy and the input take up the same space */
  font: inherit;
  padding: 0 0.25em;
  margin: 0;
  border: 1px solid grey;
  border-radius: 2px;
}

.growing-input .dummy {
  visibility: hidden;
  white-space: pre-wrap;
}
Here's an 
<span class="growing-input">
  <input type="text" value="auto-resizing input" size="1" />
  <span class="dummy"></span>
</span>; have fun!

The idea is to create a dummy element containing the same content as the input, then set the input width to match that of the dummy element. Above we use JavaScript to synchronize the text, and a display: inline-grid trick to set the input width to match.

(This approach is taken from this article; I've condensed it to the bare essentials.)

jameshfisher
  • 34,029
  • 31
  • 121
  • 167
  • I was able to simplify this solution further with content editable having two spaces. The other solution does not require a dummy span. But uses the same inline-grid magic. Added as another answer. But this was an interesting approach. – Michael Dimmitt Mar 13 '23 at 15:09
2

Here you can try something like this

EDIT: REVISED EXAMPLE (added one new solution) http://jsfiddle.net/jszjz/10/

Code explanation

var jqThis = $('#adjinput'), //object of the input field in jQuery
    fontSize = parseInt( jqThis.css('font-size') ) / 2, //its font-size
    //its min Width (the box won't become smaller than this
    minWidth= parseInt( jqThis.css('min-width') ), 
    //its maxWidth (the box won't become bigger than this)
    maxWidth= parseInt( jqThis.css('max-width') );

jqThis.bind('keydown', function(e){ //on key down
   var newVal = (this.value.length * fontSize); //compute the new width

   if( newVal  > minWidth && newVal <= maxWidth ) //check to see if it is within Min and Max
       this.style.width = newVal + 'px'; //update the value.
});

and the css is pretty straightforward too

#adjinput{
    max-width:200px !important;
    width:40px;
    min-width:40px;
    font-size:11px;
}

EDIT: Another solution is to havethe user type what he wants and on blur (focus out), grab the string (in the same font size) place it in a div - count the div's width - and then with a nice animate with a cool easing effect update the input fields width. The only drawback is that the input field will remain "small" while the user types. Or you can add a timeout : ) you can check such a kind of solution on the fiddle above too!

Pantelis
  • 6,086
  • 1
  • 18
  • 21
  • `font-size` is not a perfect solution. Not bad and certainly not worth a down vote, but try typing the letter `i` several times and you'll see very skewed results in the size. See my answer for a fix for this. – Paul Aug 24 '11 at 00:09
  • see the second example in my fiddle which doesnot uses font-size and opts for smoothness. The first one was added for speed and speed only - while maintaining what the asker wanted. Having to create a div append text to it, compute its width, etc etc every single time seems to "heavy" to me, for something that simple. – Pantelis Aug 24 '11 at 00:16
  • I'm not sure why, but your second example doesn't resize at all for me. Also keep in mind that my solution (Which does create a temporary div and such) can run about 800 times per second in a modern browser on an average up to date computer and probably doesn't get much slower than 100 times per second on 99.5% of computers that are still in use today) – Paul Aug 24 '11 at 00:33
0

Here's a method that worked for me. When you type into the field, it puts that text into the hidden span, then gets its new width and applies it to the input field. It grows and shrinks with your input, with a safeguard against the input virtually disappearing when you erase all input. Tested in Chrome. (EDIT: works in Safari, Firefox and Edge at the time of this edit)

function travel_keyup(e)
{
    if (e.target.value.length == 0) return;
    var oSpan=document.querySelector('#menu-enter-travel span');
    oSpan.textContent=e.target.value;
    match_span(e.target, oSpan);
}
function travel_keydown(e)
{
    if (e.key.length == 1)
    {
        if (e.target.maxLength == e.target.value.length) return;
        var oSpan=document.querySelector('#menu-enter-travel span');
        oSpan.textContent=e.target.value + '' + e.key;
        match_span(e.target, oSpan);
    }
}
function match_span(oInput, oSpan)
{
    oInput.style.width=oSpan.getBoundingClientRect().width + 'px';
}

window.addEventListener('load', function()
{
    var oInput=document.querySelector('#menu-enter-travel input');
    oInput.addEventListener('keyup', travel_keyup);
    oInput.addEventListener('keydown', travel_keydown);

    match_span(oInput, document.querySelector('#menu-enter-travel span'));
});
#menu-enter-travel input
{
 width: 8px;
}
#menu-enter-travel span
{
 visibility: hidden;
    position: absolute;
    top: 0px;
    left: 0px;
}
<div id="menu-enter-travel">
<input type="text" pattern="^[0-9]{1,4}$" maxlength="4">KM
<span>9</span>
</div>
Aaron Mason
  • 310
  • 3
  • 11
0

If you're allowed to use the ch measurement (monospaced) it completely solved what I was trying to do.

onChange(e => {
    e.target.style.width = `${e.target.length}ch`;
})

This was exactly what I needed but I'm not sure if it works for dynamic width font-families.

jscul
  • 748
  • 1
  • 10
  • 25
0

For those strictly looking for a solution that works for input or textarea, this is the simplest solution I've came across. Only a few lines of CSS and one line of JS.

The JavaScript sets a 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.

Chen W
  • 119
  • 2
  • 12
0

https://css-tricks.com/auto-growing-inputs-textareas had an interesting solution that requires no library: https://codepen.io/shshaw/pen/bGNJJBE

Simplified here: https://codepen.io/michaeldimmitt/pen/mdGXgjy In order to be simplified:

  • Content editable needed two spaces to start out
  • This allows the parent element to compensate with the input having a natural padding.

Note: adding size="20" to specify starting out width would be 20 characters long.


/* css */
.input-sizer {
  display: inline-grid;

  &::after {
    content: attr(data-value) '  ';
    visibility: hidden;
    white-space: pre-wrap;
  }
}

/* html */
<label class="input-sizer">
  <input  type="text" size="1"
    onInput="this.parentNode.dataset.value = this.value"
    placeholder="5"
  >
</label>
Michael Dimmitt
  • 826
  • 10
  • 23
-1

All you need to do is, get the element of the input field you want to grow as you type and in CSS, set the width of the input to auto and set a min-width to say 50px.