169

Newbie CSS question. I thought width:auto for a display:block element meant 'fill available space'. However for an <input> element this doesn't seem to be the case. For example:

<body>
  <form style='background:red'>
    <input type='text' style='background:green; display:block; width:auto'>
  </form>
</body>

Two questions then:

  1. Is there a definition of exactly what width:auto does mean? The CSS spec seems vague to me, but maybe I missed the relevant section.

  2. Is there a way to achieve my expected behavior for a input field - ie. fill available space like other block level elements do?

Thanks!

Gleb Kemarsky
  • 10,160
  • 7
  • 43
  • 68
richb
  • 4,716
  • 5
  • 24
  • 22
  • possible duplicate of [input with display:block is not a block, why not?](http://stackoverflow.com/questions/1030793/input-with-displayblock-is-not-a-block-why-not) – Phrogz Jan 07 '11 at 03:26
  • @Phrogz Yes it is a duplicate. I searched but didn't find it. Thx. – richb Jan 07 '11 at 03:44

13 Answers13

115

An <input>'s width is generated from its size attribute. The default size is what's driving the auto width.

You could try width:100% as illustrated in my example below.

Doesn't fill width:

<form action='' method='post' style='width:200px;background:khaki'>
  <input style='width:auto' />
</form>

Fills width:

<form action='' method='post' style='width:200px;background:khaki'>
  <input style='width:100%' />
</form>

Smaller size, smaller width:

<form action='' method='post' style='width:200px;background:khaki'>
  <input size='5' />
</form>

UPDATE

Here's the best I could do after a few minutes. It's 1px off in FF, Chrome, and Safari, and perfect in IE. (The problem is #^&* IE applies borders differently than everyone else so it's not consistent.)

<div style='padding:30px;width:200px;background:red'>
  <form action='' method='post' style='width:200px;background:blue;padding:3px'>
    <input size='' style='width:100%;margin:-3px;border:2px inset #eee' />
    <br /><br />
    <input size='' style='width:100%' />
  </form>
</div>
Ben
  • 54,723
  • 49
  • 178
  • 224
  • 26
    Thanks. width:100% is not the same thing tho. It will overflow the parent box if there are margins or a border. – richb Jan 07 '11 at 02:56
  • 1
    You could explicitly set these to adjust how you want, e.g. `border:2px inset #eee; margin:-2px`. Haven't tested it myself though but something along those lines. – Ben Jan 07 '11 at 02:58
  • 3
    Thanks Steve. You should also check out http://stackoverflow.com/questions/1030793/input-with-displayblock-is-not-a-block-why-not which has some other interesting ideas. – richb Jan 07 '11 at 03:45
  • No worries. Yes, there are some good ideas - there's the border solution, for one. :) – Ben Jan 07 '11 at 04:46
  • 1
    Epic solution to consistent widths via css on input fields, +1 – Stephen Sprinkle Jul 27 '12 at 01:11
  • 12
    you can use set the inputs box-sizing property to border-box to stop the border from overlapping the container. – nicholas May 06 '13 at 05:51
39

ORIGINAL answer using Angular: Because input's width is controlled by it's size attribute, this is how I initialize an input width according to its content:

<input type="text" class="form-list-item-name" [size]="myInput.value.length" #myInput>

UPDATE for JavaScript (10/01/2022): My original answer was from the time I was studying Angular. If you need pure, Vanilla JavaScript the solution is even simpler:

<input type="text" oninput="this.size = this.value.length">

Or add an "input" event listener to your input html element and run a code like this:

const myInput = document.querySelector('input');
myInput.addEventListener('input', this.typing);

(...)

typing(e) {
  e.target.setAttribute('size', e.target.value.length);
}

Obs: Depending on the browser, input may restore to its default size of something between 150px and 250px if/when size gets the 0 value. In this case, just add +1 to value.length:

<input type="text" oninput="this.size = this.value.length + 1">

OR:

typing(e) {
  e.target.setAttribute('size', e.target.value.length + 1);
}
Fabricio
  • 828
  • 8
  • 13
  • 13
    Although this `#angular` specific answer is not a valid answer to the OP's question (and I'm not upvoting), I did come to this topic just to find this particular idea. – nobug Jul 05 '19 at 10:36
  • 1
    Perfectly works with VueJs too : `` – Shadam Jun 17 '21 at 09:57
  • Watch out since `[size]="0"` will give an error. And with any solution setting size you may want to use `font-family: monospace` since `.....` is a lot narrower than `WWWWW` and `size` does not consider the contents, just the length at the current font size. – Simon_Weaver Aug 25 '21 at 19:57
  • You can just add one to avoid the error `(myInput.value?.length ?? 0) + 1` – Simon_Weaver Aug 25 '21 at 20:14
  • 1
    Note that the `size` attribute is only valid for `email`, `password`, `tel`, and `text` input types. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-size. Other input types must be controlled with CSS `width` (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number#controlling_input_size). – spiffytech Sep 11 '21 at 18:50
  • @nobug You're welcome :) – Fabricio Jan 10 '22 at 23:04
36

"Is there a definition of exactly what width:auto does mean? The CSS spec seems vague to me, but maybe I missed the relevant section."

No one actually answered the above part of the original poster's question.

Here's the answer: http://www.456bereastreet.com/archive/201112/the_difference_between_widthauto_and_width100/

As long as the value of width is auto, the element can have horizontal margin, padding and border without becoming wider than its container...

On the other hand, if you specify width:100%, the element’s total width will be 100% of its containing block plus any horizontal margin, padding and border... This may be what you want, but most likely it isn’t.

To visualise the difference I made an example: http://www.456bereastreet.com/lab/width-auto/

response
  • 361
  • 3
  • 3
12

As stated in the other answer, width: auto doesn't work due to the width being generated by the input's size attribute, which cannot be set to "auto" or anything similar.

There are a few workarounds you can use to cause it to play nicely with the box model, but nothing fantastic as far as I know.

First you can set the padding in the field using percentages, making sure that the width adds up to 100%, e.g.:

input {
  width: 98%;
  padding: 1%;
}

Another thing you might try is using absolute positioning, with left and right set to 0. Using this markup:

<fieldset>
    <input type="text" />
</fieldset>

And this CSS:

fieldset {
  position: relative;
}

input {
    position: absolute;
    left: 0;
    right: 0;
}

This absolute positioning will cause the input to fill the parent fieldset horizontally, regardless of the input's padding or margin. However a huge downside of this is that you now have to deal with the height of the fieldset, which will be 0 unless you set it. If your inputs are all the same height this will work for you, simply set the fieldset's height to whatever the input's height should be.

Other than this there are some JS solutions, but I don't like applying basic styling with JS.

Jon Raasch
  • 3,703
  • 6
  • 30
  • 32
8

If you're willing to include a little JavaScript to solve this, you can get exact sizing. This doesn't rely on approximating width with size or ems, doesn't rely on any hardcoded element widths, and works for e.g., type="number", which don't accept a size attribute.

The trick is to get your input sized exactly like a span with the same content, by actually having an invisible span with the same content.

Put your input inside a div along with a span that mirrors the input's value. Give both the input and the span the same styling, give the input 100% width, then hide the span and absolute-position the input to sit on top of the span.

This way, the container (and thus the input) are automatically sized by the visual appearance of the content of the invisible span.

codepen screenshot

https://codepen.io/spiffytech/pen/abwWRqo

<div id="relative-parent">
  <span id="size-calibration"></span>
  <input id="autosized-input" />
</div>

<style>
  #relative-parent {
    position: relative;
    /* Have some width if the input is empty */
    min-width: 1em;
    /* Adjust size to match the span */
    width: min-content;
  }

  #size-calibration {
    visibility: hidden;
    /* Prevent the span from wrapping the text when input value has multiple words, or collapsing multiple spaces into one */
    white-space: pre;
  }

  #autosized-input {
    position: absolute;
    left: 0;
    width: 100%;
  }
  
  #size-calibration, #autosized-input {
    /* Normalize styles that the browser sets differently between spans and inputs. 
    Ideally, use a "CSS reset" here. */
    font-family: "Arial";
    padding: 0;
    /* Demonstrate that this works for input with custom styles */
    font-size: 24px;
  }
</style>

<script>
  function updateSize() {
    const span = document.getElementById('size-calibration');
    const input = document.getElementById('autosized-input')
    span.innerText = input.value;
  }
  document.addEventListener('DOMContentLoaded', () => {
    const input = document.getElementById('autosized-input');
    input.oninput = updateSize;
    
    // Provide some initial content
    input.value = "I'm sized exactly right!"
    updateSize();
  })
</script>
spiffytech
  • 6,161
  • 7
  • 41
  • 57
  • 1
    hacky, but actually pretty clever) – donnikitos Jan 13 '22 at 15:48
  • 1
    @donnikitos here was a similar answer , it also has the react version ;) https://stackoverflow.com/questions/64092841/react-how-to-make-an-input-only-as-wide-as-the-amount-of-text-provided/64094013#64094013 if you have any interest . – G-Cyrillus Jul 29 '22 at 19:12
7

It may not be exactly what you want, but my workaround is to apply the autowidth styling to a wrapper div - then set your input to 100%.

mwilcox
  • 4,065
  • 23
  • 20
0

The only option I can think of is using width:100%. If you want to have a padding on the input field too, than just place a container label around it, move the formatting to that label instead, while also specify the padding to the label. Input fields are rigid.

Lajos Mészáros
  • 3,756
  • 2
  • 20
  • 26
0

Answer 1 - "response" gave a nice answer/link for it. To put it in short, "auto" is the default, so it is like removing any changes in the width of an element

Answer 2 - use width: 100% instead. It will fill the 100% of the parent container, in this case, the "form".

Akhil Gupta
  • 101
  • 1
  • 8
0

Nowdays, flex or grid makes it much easier , it overrides default style/behaviors of https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input#size which has a default value set at 20 see : https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input#size

Giving you 2 plain CSS options without requiring JavaScript nor setting width to 100% and deal with box-sizing.

flex/flex-grow
  <form style='background:red;display:flex;'>
    <input type='text' style='background:green; flex-grow:1'>
  </form>
grid
  <form style='background:red;display:grid;'>
    <input type='text' style='background:green;'>
  </form>
E_net4
  • 27,810
  • 13
  • 101
  • 139
G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
0

Using JQuery

$(document).on('input', '.input-fit-width', (e) => {
     $(e.currentTarget).attr('size',e.currentTarget.value.length);
})
shamaseen
  • 2,193
  • 2
  • 21
  • 35
0

After tried methods all above and failed, I workaround by modifying   width property in style by unit em:

tgt.style.width = `${(tgt.value.length + 1) / 2}em`
Sailist
  • 134
  • 8
0

Jquery way of adjusting size of input automatically.

In general:

$('#my_input_id').width( ($('#my_input_id').val().length) + "ch" ); 

On text input:

$(document).on("input", '#my_input_id', function () {

    $(this).width( ($(this).val().length) + "ch" ); 
});
doraemon
  • 325
  • 1
  • 4
  • 16
-2

I think the simplest solution is to set parent element's width:

form{
    width: 100%!important;
}
Youth overturn
  • 341
  • 5
  • 7