Adjust width of input field to its input
Asked Answered
I

33

283

<input type="text" value="1" style="min-width:1px;" />

This is my code and it is not working. Is there any other way in HTML, JavaScript, PHP or CSS to set minimum width?

I want a text input field with a dynamically changing width, so that the input field fluids around its contents. Every input has a built-in padding of 2em, that is the problem and second problem is that min-width ain't working on input at all.

If I set width more than it is needed than the whole program is messy, I need the width of 1px, more only if it's needed.

Interpretation answered 2/8, 2010 at 23:11 Comment(1)
Does this answer your question? Creating a textarea with auto-resizeValenti
R
146

It sounds like your expectation is that the style be applied dynamically to the width of the textbox based on the contents of the textbox. If so you will need some js to run on textbox contents changing, something like this:

<input id="txt" type="text" onkeypress="this.style.width = ((this.value.length + 1) * 8) + 'px';">

Note: this solution only works when every character is exactly 8px wide. You could use the CSS-Unit "ch" (characters) which represents the width of the character "0" in the chosen font. You can read about it here.

Rebeckarebeka answered 2/8, 2010 at 23:45 Comment(10)
@Interpretation & Tahbaza: yes, but this will only work if the width of every character is exactly 8 pixels.Moiramoirai
I agree, Marcel (and the posted code is not something I can see a use for really) but it exhibits the desired behavior. Do you know how to calculate the actual width of rendered text taking into account font, size, weight, kerning, etc.? If so, please share as I would find that bit of code useful on occasion.Rebeckarebeka
– I already thought that it was only an example. It's not that difficult to calculate the width of the input, though. Have a look at my answer.Moiramoirai
em units are the width of the character "m". This would be useful for calculating the width of a string. I've seen cross-browser issues with em units applied to some attributes though so do test it.Shererd
Hi its perfect answer , but when i try to implement dynamically as $("#QueAnswerSpan").append('<input type="text" class="QueAnswer taaray_input_box width_1 left" id="ans'+countFill+'" onkeypress="this.style.width = ((this.value.length + 1) * 8) + '"px"';"/>'); it shows me error at px and break the string as ---><input id="ans1" class="QueAnswer taaray_input_box width_1 left" type="text" px";"=" onkeypress="this.style.width = ((this.value.length + 1) * 8) + ">Unsaid
@Anurag: you're trying to delimit ' within js inside of a ' delimited string. You need \ to indicate the ' literal, like this: $("#QueAnswerSpan").append('<input type="text" class="QueAnswer taaray_input_box width_1 left" id="ans'+countFill+'" onkeypress="this.style.width = ((this.value.length + 1) * 8) + \'"px"\';"/>');Rebeckarebeka
Works very nice for me, especially since I am using a monospaced font anyway in the input field. Only adjusting the constant character width integer. I also added another 5 pixel to the calculated width.Chinaman
you have forgotten the PASTE event my friend ;) you should change it to onInputSavagism
@Savagism see my answer for a more modern and accurate approachExanimate
This solution is not applicable when higher or lower font-size because when font size is 24px then character size will be definitely not 8px and different browsers render different width for same size of font.Giralda
F
246

In modern browser versions, CSS unit ch is also available. To my understanding, it is font-independent unit, where 1ch equals to width of character 0 (zero) in any given font.

Thus, something as simple as following could be used as resize function, by binding to the input event:

var input = document.querySelector('input'); // get the input element
input.addEventListener('input', resizeInput); // bind the "resizeInput" callback on "input" event
resizeInput.call(input); // immediately call the function

function resizeInput() {
  this.style.width = this.value.length + "ch";
}
input{ font-size:1.3em; padding:.5em; }
<label>Text
  <input>
</label>

That example would resize the input to length of the value + 2 characters extra.

One potential problem with the unit ch is that in many fonts (i.e. Helvetica) the width of the character "m" exceeds the width of the character 0 and the character "i" is much narrower. 1ch is usually wider than the average character width, usually by around 20-30% according to this post.

Florencia answered 19/4, 2017 at 7:12 Comment(9)
I'm surprised this comment isn't voted up higher, because it is a great solution and about 95% (1) of browsers support this.Newsom
I love this answer. ch is great; I wonder what other units I'm late in discovering. Thanks.Maryannemarybella
This is not a good answer at all, since the measurement is not accurate and does not take into account styles that might occur on the input element, such as font-style, letter-spacing and so on. I've edited the answer with a live demo.Savagism
Works great if you use a monospaced font. With other fonts your mileage may vary as it uses the character width of the 0 character apparently.Scope
This is really great. Although note that it sometimes bugs out/ doesn't work as I expected when a . is encountered.Erewhile
This works perfectly if you set the box-sizing on the input to 'content-box'. That way it ignores the padding when you set the size.Ioab
how about there is current width specified. Scenario is, there is width: 100px, if I type one character, it goes lesser 100px which is 1ch.Stronski
Doesn't work for punctuation and uppercase lettersKato
Something as simple as: oninput="this.style.maxWidth = (this.value.length + 10) + \'ch\';" seems to work too.Substratosphere
R
146

It sounds like your expectation is that the style be applied dynamically to the width of the textbox based on the contents of the textbox. If so you will need some js to run on textbox contents changing, something like this:

<input id="txt" type="text" onkeypress="this.style.width = ((this.value.length + 1) * 8) + 'px';">

Note: this solution only works when every character is exactly 8px wide. You could use the CSS-Unit "ch" (characters) which represents the width of the character "0" in the chosen font. You can read about it here.

Rebeckarebeka answered 2/8, 2010 at 23:45 Comment(10)
@Interpretation & Tahbaza: yes, but this will only work if the width of every character is exactly 8 pixels.Moiramoirai
I agree, Marcel (and the posted code is not something I can see a use for really) but it exhibits the desired behavior. Do you know how to calculate the actual width of rendered text taking into account font, size, weight, kerning, etc.? If so, please share as I would find that bit of code useful on occasion.Rebeckarebeka
– I already thought that it was only an example. It's not that difficult to calculate the width of the input, though. Have a look at my answer.Moiramoirai
em units are the width of the character "m". This would be useful for calculating the width of a string. I've seen cross-browser issues with em units applied to some attributes though so do test it.Shererd
Hi its perfect answer , but when i try to implement dynamically as $("#QueAnswerSpan").append('<input type="text" class="QueAnswer taaray_input_box width_1 left" id="ans'+countFill+'" onkeypress="this.style.width = ((this.value.length + 1) * 8) + '"px"';"/>'); it shows me error at px and break the string as ---><input id="ans1" class="QueAnswer taaray_input_box width_1 left" type="text" px";"=" onkeypress="this.style.width = ((this.value.length + 1) * 8) + ">Unsaid
@Anurag: you're trying to delimit ' within js inside of a ' delimited string. You need \ to indicate the ' literal, like this: $("#QueAnswerSpan").append('<input type="text" class="QueAnswer taaray_input_box width_1 left" id="ans'+countFill+'" onkeypress="this.style.width = ((this.value.length + 1) * 8) + \'"px"\';"/>');Rebeckarebeka
Works very nice for me, especially since I am using a monospaced font anyway in the input field. Only adjusting the constant character width integer. I also added another 5 pixel to the calculated width.Chinaman
you have forgotten the PASTE event my friend ;) you should change it to onInputSavagism
@Savagism see my answer for a more modern and accurate approachExanimate
This solution is not applicable when higher or lower font-size because when font size is 24px then character size will be definitely not 8px and different browsers render different width for same size of font.Giralda
M
79

To calculate the width of the current input, you'll have to embed it in a temporary span element, attach that thing to the DOM, get the computed width (in pixels) using the scrollWidth property and remove the span again. Of course you'll have to ensure that the same font family, font size, etc., is used in the input as well as in the span element. Therefore I assigned the same class to them.

I attached the function to the keyup event, as on keypress the input character is not yet added to the input value, so that will result in the wrong width. Unfortunately, I don't know how to get rid of the scrolling of the input field (when adding characters to the end of the field); it scrolls, because the character is added and shown before adjustWidthOfInput() is called. And, as said, I can't do this the other way round because then you'll have the value of the input field before the pressed character is inserted. I'll try to solve this issue later.

BTW, I only tested this in Firefox (3.6.8), but you'll get the point, I hope.

var inputEl = document.getElementById("theInput");
    
function getWidthOfInput() {
  var tmp = document.createElement("span");
  tmp.className = "input-element tmp-element";
  tmp.innerHTML = inputEl.value.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  document.body.appendChild(tmp);
  var theWidth = tmp.getBoundingClientRect().width;
  document.body.removeChild(tmp);
  return theWidth;
}

function adjustWidthOfInput() {
  inputEl.style.width = getWidthOfInput() + "px";
}

adjustWidthOfInput();
inputEl.onkeyup = adjustWidthOfInput;
body {
  background: #666;
}

.input-element {
  border: 0;
  padding: 2px;
  background: #fff;
  font: 12pt sans-serif;
}

.tmp-element {
  visibility: hidden;
  white-space: pre;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Get/set width of &lt;input&gt;</title>
  </head>
  <body>
    <input id="theInput" type="text" class="input-element" value="1">
  </body>
</html>
Moiramoirai answered 3/8, 2010 at 11:24 Comment(5)
I liked your answer and even implemented it using jQuery/Underscore: gist.github.com/3745941. However, I could make certain assumptions: (1) the width of any character is no more than 14px, (2) it's acceptable to have the input element extend 14px past the value of the element. I have this attached to a keydown event and it's working great!Ostmark
You should add white-space: pre; into css this way: .tmp-element{ visibility: hidden; white-space: pre;}. Otherwise white-spaces are combined and the width calculation fails. <input> and <span> behaves differently regarding to white-space handling, <input> retains white-spaces and <span> combines sequential white-spaces into one if white-space: pre; is not added.Bruns
tmp.getBoundingClientRect().width is better than tmp.scrollWidth, because that sometimes returns 0Becerra
@Becerra Thanks, I adjusted my answer, but there are already better answers here.Moiramoirai
Better? I wouldn't say so. Marked answer lacks precision, others (+ here 1 and here 2) uses jQuery - I don't because of React. Canvas can't give you text height AFAIK. Idea: issue with spaces could be also solved with: .replace(/ /g, "&nbsp;")Verona
A
65

Here is a solution without monospaced font needed, with only a very small piece code of javascript, does not need to calculate computed styles, and even supports IME, supports RTL text.

// copy the text from input to the span 
document.addEventListener('input', event => {
  const target = event.target;
  if (!(target instanceof HTMLElement)) return;
  if (!target.matches('.resize-input')) return;
  target.previousElementSibling.textContent = target.value;
});

Array.from(document.querySelectorAll('.resize-input')).forEach(input => {
  input.previousElementSibling.textContent = input.value;
});
.resize-container {
    display: inline-block;
    position: relative;
}

.resize-input,
.resize-text {
    margin: 0;
    padding: 2px 10px;
    border: 1px solid #ccc;
    border-radius: 3px;
    height: 36px;
    font: 20px/20px sans-serif;
}

.resize-text {
    padding-right: 20px;
    display: inline-block;
    visibility: hidden;
    white-space: pre;
}
    
.resize-input {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
}
<div class="resize-container">
  <span class="resize-text"></span>
  <input class="resize-input" value="some text" autofocus />
</div>

Here are steps

  1. Wrap the <input> into a container. And add an extra <span> next to it.
  2. Write JavaScript so it regular copy text from the input to the span whenever the input value is changed. This could be done by listening to the input event. If you are using Vanilla JavaScript, you can use event delegate like what this post do. If you are using frameworks like React or Vue, you can simply bind the span with same value of the input have.
  3. Write CSS, the container adjust its size based on the text span. The text span and input have same style so the size of span is the expected size of Input. Finally, you just need to make the input have absolute position to the container. So it adjust its size based on the text span.

Vue note: you should listen to @input instead of use v-model so it can adjust size correctly during IME composition. Live Demo

Ascender answered 30/12, 2016 at 2:47 Comment(2)
For multiple inputs see https://mcmap.net/q/107711/-adjust-width-of-input-field-to-its-inputHoloenzyme
I love this, it is simple and elegant and works very well.Dumbfound
U
32

Here's a modification of Lyth's answer that takes into account:

  • Deletion
  • Initialisation
  • Placeholders

It also allows for any number of input fields! To see it in action: http://jsfiddle.net/4Qsa8/

Script:

$(document).ready(function () {
    var $inputs = $('.resizing-input');

    // Resize based on text if text.length > 0
    // Otherwise resize based on the placeholder
    function resizeForText(text) {
        var $this = $(this);
        if (!text.trim()) {
            text = $this.attr('placeholder').trim();
        }
        var $span = $this.parent().find('span');
        $span.text(text);
        var $inputSize = $span.width();
        $this.css("width", $inputSize);
    }

    $inputs.find('input').keypress(function (e) {
        if (e.which && e.charCode) {
            var c = String.fromCharCode(e.keyCode | e.charCode);
            var $this = $(this);
            resizeForText.call($this, $this.val() + c);
        }
    });

    // Backspace event only fires for keyup
    $inputs.find('input').keyup(function (e) { 
        if (e.keyCode === 8 || e.keyCode === 46) {
            resizeForText.call($(this), $(this).val());
        }
    });

    $inputs.find('input').each(function () {
        var $this = $(this);
        resizeForText.call($this, $this.val())
    });
});

Style:

.resizing-input input, .resizing-input span {
    font-size: 12px;
    font-family: Sans-serif;
    white-space: pre;
    padding: 5px;
}

HTML:

<div class="resizing-input">
    <input type="text" placeholder="placeholder"/>
    <span  style="display:none"></span>
</div>

$(document).ready(function() {
  var $inputs = $('.resizing-input');

  // Resize based on text if text.length > 0
  // Otherwise resize based on the placeholder
  function resizeForText(text) {
    var $this = $(this);
    if (!text.trim()) {
      text = $this.attr('placeholder').trim();
    }
    var $span = $this.parent().find('span');
    $span.text(text);
    var $inputSize = $span.width();
    $this.css("width", $inputSize);
  }

  $inputs.find('input').keypress(function(e) {
    if (e.which && e.charCode) {
      var c = String.fromCharCode(e.keyCode | e.charCode);
      var $this = $(this);
      resizeForText.call($this, $this.val() + c);
    }
  });

  // Backspace event only fires for keyup
  $inputs.find('input').keyup(function(e) {
    if (e.keyCode === 8 || e.keyCode === 46) {
      resizeForText.call($(this), $(this).val());
    }
  });

  $inputs.find('input').each(function() {
    var $this = $(this);
    resizeForText.call($this, $this.val())
  });
});
.resizing-input input,
.resizing-input span {
  font-size: 12px;
  font-family: Sans-serif;
  white-space: pre;
  padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="resizing-input">
  First:
  <input type="text" placeholder="placeholder" />
  <span style="display:none"></span>
</div>
<br>
Uppish answered 17/6, 2014 at 9:18 Comment(4)
Hi @Michael. Your code works great but I have a problem. One of my field is a Google place autocmplete field. And when I pick the right address from a dropped down menu, the width of the input field is not changed according the length of the address chosen. Is there a simple fix for it ?Vela
@Vela listening to the change or input event would sufficeMukerji
I had issues with the span's display:none styling since it registered the span as having 0 width. Better to use absolute positioning with visibility:hidden for the span.Maximilien
Hi @Mukerji , thanks for the tip. By any chance would you know how to tweak the original script with this setup ? (my knowledge in Js/jquery is very very limited)Vela
A
31

This is an Angular-specific answer, but this worked for me and has been very satisfying in terms of its simplicity and ease-of-use:

<input [style.width.ch]="value.length" [(ngModel)]="value" />

It automatically updates via the character units in Jani's answer.

Autopsy answered 7/12, 2017 at 21:49 Comment(4)
This. Is. Epic.Journalist
Seriously. Magical.Paw
For people addicted to frameworks so much that they forget the native code is actually simpler <input oninput="this.style.width=this.value.length+'ch'">Dorelle
this does not work if we enter capital letters in the input field.Boelter
S
24

Just adding on top of other answers.

I noticed that nowadays in some browsers the input field has a scrollWidth. Which means:

this.style.width = this.scrollWidth + 'px';

should work nicely. tested in chrome, firefox and safari.

For deletion support, you can add '=0' first and then readjust.

this.style.width = 0; this.style.width = this.scrollWidth + 'px';
Strohbehn answered 2/7, 2013 at 5:42 Comment(2)
How do you apply the resize command to the initial loading of the page? I tried onload="..." but this didn't workAlroi
Here is a demo of how to do it with react (very simple): stackblitz.com/edit/react-input-auto-width?file=App.tsxFoudroyant
D
23

FOR A NICER LOOK&FEEL

You should use jQuery keypress() event in combination with String.fromCharCode(e.which) to get the pressed character. Hence you can calculate what your width will be. Why? Because it will look a lot more sexy :)

Here is a jsfiddle that results in a nice behaviour compared to solutions using the keyup event : http://jsfiddle.net/G4FKW/3/

Below is a vanilla JS which listens to the input event of an <input> element and sets a span sibling to have the same text value in order to measure it.

document.querySelector('input').addEventListener('input', onInput)

function onInput(){
    var spanElm = this.nextElementSibling;
    spanElm.textContent = this.value; // the hidden span takes the value of the input; 
    this.style.width = spanElm.offsetWidth + 'px'; // apply width of the span to the input
};
/* it's important the input and its span have same styling */
input, .measure {
    padding: 5px;
    font-size: 2.3rem;
    font-family: Sans-serif;
    white-space: pre; /* white-spaces will work effectively */
}

.measure{  
  position: absolute;
  left: -9999px;
  top: -9999px;
}
<input type="text" />
<span class='measure'></span>
Danicadanice answered 13/2, 2013 at 14:29 Comment(4)
Looks nice, but it doesn't take Backspace and Delete into account.Moiramoirai
Yes, for this you need keydown event. You can add specific handling for backspace and delete by doing so (e.which === 46 for delete, e.which === 8 for backspace). But you still need keypress event to have access to e.charCode for the rest.Danicadanice
If you're not supporting IE8 you can use the oninput event instead of checking e.which: developer.mozilla.org/en-US/docs/Web/Events/inputElegit
I've edited your answer to eliminate jQuery and use only vanilla JS. mind that this solution is far from optimal because you want a generic solution that doesn't involve in manually coding a span and css for it.Savagism
A
20

Here is an alternative way to solve this using a DIV and the 'contenteditable' property:

HTML:

<div contenteditable = "true" class = "fluidInput" data-placeholder = ""></div>

CSS: (to give the DIV some dimensions and make it easier to see)

.fluidInput {

    display         : inline-block;
    vertical-align  : top;

    min-width       : 1em;
    height          : 1.5em;

    font-family     : Arial, Helvetica, sans-serif;
    font-size       : 0.8em;
    line-height     : 1.5em;

    padding         : 0px 2px 0px 2px;
    border          : 1px solid #aaa;
    cursor          : text;
}


.fluidInput * {

    display         : inline;

}


.fluidInput br  {

    display         : none;

}


.fluidInput:empty:before {

    content         : attr(data-placeholder);
    color           : #ccc;

}

Note: If you are planning on using this inside of a FORM element that you plan to submit, you will need to use Javascript / jQuery to catch the submit event so that you can parse the 'value' ( .innerHTML or .html() respectively) of the DIV.

Argenteuil answered 9/1, 2017 at 10:9 Comment(3)
This deserves more upvotes. It's elegant and simple (as long as using JS for form validations isn't a problem for you).Trapezohedron
But it's not an input!Squeamish
I know this is an old post, but while this method does produce a good result, using contenteditable can create additional challenges that require custom solutions where preexisting solutions already exist, i.e. form validation. I tried using div[contenteditable] to solve OP's challenge, but it ended up being more headache than it was worth as the number of "gotchas" kept increasing.Weissmann
E
18

This answer provides one of the most accurate methods of retrieving text width available in the browser and is more accurate than the accepted answer. It uses the canvas html5 element and unlike other answers does not add the element into the DOM and thus avoids any reflow issues caused by excessively adding elements to the DOM.

Read more about the Canvas element here in relation to text width.

NOTE: According to MDN the shorthand versions of the getPropertyValue() method such as font can be unreliable. I'd recommend getting the values singularly to improve compatibility. I only used it here for speed.

/**
 * returns the width of child text of any DOM node as a float
 */
function getTextWidth(el) {
  // uses a cached canvas if available
  var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
  var context = canvas.getContext("2d");
  // get the full font style property
  var font = window.getComputedStyle(el, null).getPropertyValue('font');
  var text = el.value;
  // set the font attr for the canvas text
  context.font = font;
  var textMeasurement = context.measureText(text);
  return textMeasurement.width;
}

var input = document.getElementById('myInput');
// listen for any input on the input field
input.addEventListener('input', function(e) {
  var width = Math.floor(getTextWidth(e.target));
  // add 10 px to pad the input.
  var widthInPx = (width + 10) + "px";
  e.target.style.width = widthInPx;
}, false);
#myInput {
  font: normal normal 400 normal 18px / normal Roboto, sans-serif;
  min-width: 40px;
}
<input id="myInput" />
Exanimate answered 16/5, 2018 at 0:7 Comment(2)
Best answer on here by far. Performant and accurate. Only thing is you may need to add an offset to the calculated widthAdularia
Love this answer, definitely best by far! Here's a fiddle, adding a bit of jQuery to improve on it ... jsfiddle.net/designosis/ran0vdujStreamer
F
10

You can set an input's width using the size attribute as well. The size of an input determines it's width in characters.

An input could dynamically adjust it's size by listening for key events.

For example

$("input[type='text']").bind('keyup', function () {
    $(this).attr("size", $(this).val().length );
});

JsFiddle here

Farley answered 13/2, 2014 at 23:31 Comment(0)
T
8

You could do something like this

// HTML
<input id="input" type="text" style="width:3px" />
// jQuery
$(function(){
  $('#input').keyup(function(){
    $('<span id="width">').append( $(this).val() ).appendTo('body');
    $(this).width( $('#width').width() + 2 );
    $('#width').remove();
  });
});

Troth answered 2/8, 2010 at 23:44 Comment(1)
If you increase the size on keyup it will result in an ugly behaviour. Input needs to increase its size just before the character comes into it for more comfort.Danicadanice
S
7

You can just set size attribute. If you're using one of reactive frameworks, the following will be enough:

<input size="{{ yourValue.length }}" [value]="yourValue" />

but if you use pure js, you should set event handlers, like:

<input oninput="this.setAttribute('size', this.value.length)" />

Sanyu answered 13/7, 2020 at 17:54 Comment(0)
T
5

It's worth noting that a nice-looking resize can be done when the font is monospaced, so we can perfectly resize the input element using the ch unit.

Also in this approach we can update the width of the field by just updating a CSS variable (custom property) on input event and we should also take care of already pre-filled input on DOMContentLoaded event

Codepen demo


Markup

<input type="text" value="space mono font" class="selfadapt" />

CSS

:root { --size: 0; }

.selfadapt {
   padding: 5px;
   min-width: 10ch;
   font-family: "space mono";
   font-size: 1.5rem;
   width: calc(var(--size) * 1ch);
}

As a root variable we set --size: 0: this variable will contain the length of the input and it will be multiplied by 1ch inside the calc() expression. By default we could also set a min-width, e.g. 10ch

The Javascript part reads the length of the value inserted and updates the variable --size:

JS

let input = document.querySelector('.selfadapt');
let root  = document.documentElement.style;

/* on input event auto resize the field */
input.addEventListener('input', function() {
   root.setProperty('--size', this.value.length );
});

/* resize the field if it is pre-populated */
document.addEventListener("DOMContentLoaded", function() {
   root.setProperty('--size', input.value.length);
});

of course this still works even if you don't use monospaced font, but in that case you will need to change the calc() formula by multiplying the --size variable by another value (which it's strictly dependent on the font-family and font-size) different than 1ch.

Toussaint answered 4/6, 2018 at 10:56 Comment(0)
N
4

Here is a plain JS and a jQuery plugin I wrote that will handle resizing an input element using a canvas and the font size / family to determine the actual string length when rendered. (only works in > IE9, chrome, safari, firefox, opera and most other major browsers that have implemented the canvas element).

PlainJS:

function autoSize(input, o) {
    o || (o = {});
    o.on || (o.on = 'keyup');

    var canvas = document.createElement('canvas');
    canvas.setAttribute('style', 'position: absolute; left: -9999px');
    document.body.appendChild(canvas);

    var ctx = canvas.getContext('2d');

    input.addEventListener(o.on, function () {
        ctx.font = getComputedStyle(this,null).getPropertyValue('font');
        this.style.width = ctx.measureText(this.value + '  ').width + 'px';
    });
}

//Usage
autoSize(document.getElementById('my-input'));

jQuery Plugin:

$.fn.autoSize = function(o) {
  o = $.extend({}, {
    on: 'keyup'
  }, o);

  var $canvas = $('<canvas/>').css({position: 'absolute', left: -9999});
  $('body').append($canvas);

  var ctx = $canvas[0].getContext('2d');

  return this.on(o.on, function(){
    var $this = $(this);
    ctx.font = $this.css('font');
    $this.width(ctx.measureText($this.val()).width + 'px');
  })
}

//Usage:
$('#my-input').autoSize();

Note: this will not handle text-transforms, line spacing and letter spacing, and probably some other text size changing properties. To handle text-transform property set and adjust the text value to match that property. The others are probably fairly straight forward. I will implement if this starts gaining some traction...

Nisa answered 1/9, 2015 at 17:17 Comment(0)
H
4

Here is my React solution, it works with any font size, just make sure you have a monospace font (all font character widths are the same on monospace fonts) like i have in my solution, and it will work perfectly.

JS:

const [value, setValue] = useState(0)

HTML:

<input value={value} onChange={(e) => {setValue(e.target.value)}} style={{width: `${value.toString().length}`ch}}/>

CSS:

@import url("https://fonts.googleapis.com/css2?family=B612+Mono&display=swap");    
input{
    font-family: "B612 Mono", monospace;
}
Hookworm answered 14/8, 2021 at 0:3 Comment(0)
S
3

A bullet-proof, generic way has to:

  1. Take into account all possible styles of the measured input element
  2. Be able to apply the measurement on any input without modifying the HTML or

Codepen demo

var getInputValueWidth = (function(){
  // https://mcmap.net/q/42170/-set-copy-javascript-computed-style-from-one-element-to-another
  function copyNodeStyle(sourceNode, targetNode) {
    var computedStyle = window.getComputedStyle(sourceNode);
    Array.from(computedStyle).forEach(key => targetNode.style.setProperty(key, computedStyle.getPropertyValue(key), computedStyle.getPropertyPriority(key)))
  }
  
  function createInputMeassureElm( inputelm ){
    // create a dummy input element for measurements
    var meassureElm = document.createElement('span');
    // copy the read input's styles to the dummy input
    copyNodeStyle(inputelm, meassureElm);
    
    // set hard-coded styles needed for propper meassuring 
    meassureElm.style.width = 'auto';
    meassureElm.style.position = 'absolute';
    meassureElm.style.left = '-9999px';
    meassureElm.style.top = '-9999px';
    meassureElm.style.whiteSpace = 'pre';
    
    meassureElm.textContent = inputelm.value || '';
    
    // add the meassure element to the body
    document.body.appendChild(meassureElm);
    
    return meassureElm;
  }
  
  return function(){
    return createInputMeassureElm(this).offsetWidth;
  }
})();


// delegated event binding
document.body.addEventListener('input', onInputDelegate)

function onInputDelegate(e){
  if( e.target.classList.contains('autoSize') )
    e.target.style.width = getInputValueWidth.call(e.target) + 'px';
}
input{ 
  font-size:1.3em; 
  padding:5px; 
  margin-bottom: 1em;
}

input.type2{
  font-size: 2.5em;
  letter-spacing: 4px;
  font-style: italic;
}
<input class='autoSize' value="type something">
<br>
<input class='autoSize type2' value="here too">
Savagism answered 16/5, 2018 at 11:46 Comment(0)
L
3

Quite simple:

oninput='this.style.width = (this.scrollWidth - N) + "px";'

Where N is some number (2 in the example, 17 on something I'm developing) that is determined experimentally.

Subtracting N prevents this strange extrenuous space from accumulating long before the text reaches the end of the text box.

Compare. Pay careful attention to how the size changes after even just the first character.

<p>Subtracting N:</p>    
<input type="text" placeholder="enter lots of text here" oninput='this.style.width = (this.scrollWidth-2) + "px";'>

<p>Not Subtracting N:</p>    
<input type="text" placeholder="enter lots of text here" oninput='this.style.width = (this.scrollWidth) + "px";'>
Lyda answered 3/2, 2020 at 10:29 Comment(1)
I haven't tested extensively with this, but you could just change oninput='this.style.width = (this.scrollWidth-2) + "px";' by oninput='this.style.width = 0; this.style.width = this.scrollWidth + "px";' to get rid of the space accumulatingHemichordate
O
2

You can do it even simpler in angularjs using the built-in ng-style directive.

In your html:

  <input ng-style="inputStyle(testdata)" ng-model="testdata" />

In your controller:

 $scope.testdata = "whatever";

 $scope.inputStyle = function(str) {
    var charWidth = 7;
    return  {"width": (str.length +1) * charWidth + "px" };
    };

In your css:

input { font-family:monospace; font-size:12px; }

Adjust the charWidth to match the width of your font. It seems to be 7 at a font-size of 12px;

Oleaster answered 20/7, 2013 at 2:22 Comment(3)
This is cool and handy to know, but adding angularjs to an app to dynamically resize an input element is really serious overkill especially when OP asked for "HTML, JavaScript, PHP or CSS" solutions. One could argue that jQuery is overkill for this, too, but--unlike Angular--being a DOM-manipulation toolkit is core to jQuery's purpose.Saccharometer
How am I supposed to know the width of the character being typed? It's not easy to assume it is a mono-sized font.Legere
You can't require a monospaced font, and there's no need for Angular. brendan's answer does basically the same.Ebullition
H
2

If you use Bootstrap, it could be done very easily:

<div contenteditable="true" class="form-control" style="display: inline"></div>

You will just need to fetch div's content and put it in a hidden input before submitting the form.

Hilariahilario answered 9/7, 2018 at 12:54 Comment(1)
Note that this solution could have issues with copy/paste content, especially if pasted content has html. And it could be unresolvable in IEClotilda
C
2

Here is my 2 cents. Create an empty invisible div. Fill it with the input content and return the width to the input field. Match text styles between each box.

$(".answers_number").keyup(function(){
    $( "#number_box" ).html( $( this ).val() );
    $( this ).animate({
        width: $( "#number_box" ).width()+20
        }, 300, function() {
    });
});
#number_box {
   position: absolute;
   visibility: hidden;
   height: auto;
   width: auto;
   white-space: nowrap;
   padding:0 4px;
   /*Your font styles to match input*/
   font-family:Arial;
   font-size: 30px; 
}
    
.answers_number {
   font-size: 30px; 
   font-family:Arial;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="number" class="answers_number" />
<div id="number_box">
</div>
Carmon answered 21/11, 2018 at 0:19 Comment(0)
B
2

Here's a simple function to get what is needed:

function resizeInput() {
    const input = document.getElementById('myInput');
    input.style.width = `${input.scrollWidth}px`;
};
Boyfriend answered 18/12, 2020 at 12:2 Comment(1)
Thanks. But that only works for entering text, not for deleting text.Bunkum
E
2

Svelte version:

<input type="text" style="width: {tag.length}ch" bind:value={tag} />
Eccentricity answered 15/5, 2021 at 17:15 Comment(1)
any plain CSS / javascript native versions?Revival
D
1

I think you're misinterpreting the min-width CSS property. min-width is generally used to define a minimum DOM width in a fluid layout, like:

input {
  width: 30%;
  min-width: 200px;
}

That would set the input element to a minimum width of 200 pixels. In this context, "px" stands for "pixels".

Now, if you're trying to check to make sure that input field contains at least one character when a user submits it, you'll need to do some form validation with JavaScript and PHP. If that is indeed what you're attempting to do, I'll edit this answer and do my best to help you out.

Dessiedessma answered 2/8, 2010 at 23:28 Comment(7)
i know what px means and i know javascript has a function for min-width but it ain't working for input text.Interpretation
@Kerc: “javascript has a function for min-width” - which one? What do you mean? – “it ain't working” is never a good description of a problem. Try to elaborate on the intended behaviour.Moiramoirai
Did you even try my example, than you see that it ain't 1 px and yours ain't also. This ain't going to work no matter if i write it in html, cssInterpretation
Perhaps there is a php function for this, anybody knows ?Interpretation
I did try your example, but I assumed that a 1 pixel wide input field wasn't what you were looking for. If it is, try <input type="text" value="1" style="width:1px;" />Dessiedessma
than it won't expand and when i print out my calculation something will missInterpretation
I see what you're trying to do now, and Tahbaza's and user338128's answers seem to fit the bill. In the future, please be more clear with the effect that you're trying to achieve. Your original question gave no indication that you wanted the input field to expand as the user typed.Dessiedessma
H
1

I really liked Lyth's answer, but also really wanted it to:

  1. Handle backspace and delete
  2. Not require you to manually add an adjacent tag.
  3. Enforce a min width.
  4. Automatically be applied to elements with a specific class

I adapted his JSFiddle and came up with this. One improvement not present in this fiddle would be to use something like the jQuery CSS Parser to actually read the initial width from the input.textbox-autosize rule, and use that as the minWidth. Right I'm simply using an attribute on the , which makes for a compact demo but is not ideal. as it requires an extra attribute on each input. You might also just want to put the minWidth as 100 right in the JavaScript.

HTML:

<div id='applicationHost'>
<div>Name:   <input class='textbox-autosize' data-min-width='100' type="text" /></div>
<div>Email:  <input class='textbox-autosize' data-min-width='100' type="email" /></div>
<div>Points: <input class='textbox-autosize' data-min-width='100' type="number" /></div>
</div>

CSS:

#applicationHost {
    font-family: courier;
    white-space: pre;
}

input.textbox-autosize, span.invisible-autosize-helper {
    padding:0;
    font-size:12px;
    font-family:Sans-serif; 
    white-space:pre;
}
input.textbox-autosize {
    width: 100px; /* Initial width of textboxes */
}

/*
In order for the measurements to work out, your input and the invisible
span need to have the same styling.
*/

JavaScript:

$('#applicationHost').on('keyup', '.textbox-autosize', function(e) {
    // Add an arbitary buffer of 15 pixels.
    var whitespaceBuffer = 15;
    var je = $(this);
    var minWidth = parseInt(je.attr('data-min-width'));
    var newVal = je.val();
    var sizingSpanClass = 'invisible-autosize-helper';
    var $span = je.siblings('span.' + sizingSpanClass).first();
    // If this element hasn't been created yet, we'll create it now.
    if ($span.length === 0) {
        $span = $('<span/>', {
            'class': sizingSpanClass,
            'style': 'display: none;'
        });
        je.parent().append($span);
    }
    $span = je.siblings('span').first();
    $span.text(newVal) ; // the hidden span takes 
    // the value of the input
    $inputSize = $span.width();
    $inputSize += whitespaceBuffer;
    if($inputSize > minWidth)
        je.css("width", $inputSize) ; // apply width of the span to the input
    else
        je.css("width", minWidth) ; // Ensure we're at the min width
});
Hinshaw answered 26/6, 2014 at 21:16 Comment(0)
C
1

Better is onvalue:

<input id="txt" type="text" onvalue="this.style.width = ((this.value.length + 1) * 8) + 'px';">

It also involves pasting, dragging and dropping, etc.

Crystalloid answered 11/4, 2017 at 20:3 Comment(0)
K
1

The best solution is <input ... size={input.value.length} />

Kohler answered 20/2, 2021 at 12:0 Comment(1)
this should be the accepted answer. Simple and elegant. Thank you.Batfish
L
0

I just spend some time figuring out how to do it.
Actually the simplest way I found is to move input value to span just before the input, keeping input 1 symbol width. Though I can't be sure that it fit for your initial need.
Maybe it some extra code, but in react+flux based application it is quite natural solution.

Lesleelesley answered 8/1, 2016 at 20:14 Comment(0)
D
0

Based off Michael's answer, I have created my own version of this using jQuery. I think it is a cleaner/shorter version of most answers here and it seems to get the job done.

I am doing the same thing as most of the people here by using a span to write the input text into then getting the width. Then I am setting the width when the actions keyup and blur are called.

Here is a working codepen. This codepen shows how this can be used with multiple input fields.

HTML Structure:

<input type="text" class="plain-field" placeholder="Full Name">
<span style="display: none;"></span>

jQuery:

function resizeInputs($text) {
    var text = $text.val().replace(/\s+/g, ' '),
        placeholder = $text.attr('placeholder'),
        span = $text.next('span');
        span.text(placeholder);
    var width = span.width();

    if(text !== '') {
        span.text(text);
    var width = span.width();
    }

    $text.css('width', width + 5);
};

The function above gets the inputs value, trims the extra spaces and sets the text into the span to get the width. If there is no text, it instead gets the placeholder and enters that into the span instead. Once it enters the text into the span it then sets the width of the input. The + 5 on the width is because without that the input gets cut off a tiny bit in the Edge Browser.

$('.plain-field').each(function() {
    var $text = $(this);
    resizeInputs($text);
});

$('.plain-field').on('keyup blur', function() {
    var $text = $(this);
    resizeInputs($text);
});

$('.plain-field').on('blur', function() {
    var $text = $(this).val().replace(/\s+/g, ' ');
    $(this).val($text);
});

If this could be improved please let me know as this is the cleanest solution I could come up with.

Decaffeinate answered 7/2, 2017 at 2:16 Comment(0)
C
0

You would like to change the size attribute as the text changes.

# react

const resizeInput = (e) => {
  e.target.setAttribute('size', e.target.value.length || 1);
}

<input 
  onChange={resizeInput}
  size={(propertyInput.current && propertyInput.current.value.length) || 1}
  ref={propertyInput} 
/>

  
Compendium answered 16/12, 2020 at 10:16 Comment(0)
H
0

Update for answer https://mcmap.net/q/107711/-adjust-width-of-input-field-to-its-input

If you have multiple inputs and do not want to repeat input text in spans:

  $(() => {
    $('.container input').on('input', (e) => {
      $(e.target).prev().text($(e.target).val())
    })  
    $('.container input').each((idx, elem) => {
      $(elem).prev().text($(elem).val())
    })
  })
Holoenzyme answered 18/5, 2022 at 9:50 Comment(0)
H
0

This is a generalization of Matthew Brent's excellent canvas-based solution. It works with select elements and generic div's in addition to input elements. It also accounts for letter spacing.

It is written in Typescript but could be trivially adapted to JS.

/** Cached canvas used by adjustWidthToFitTextLength(). */
let textMeasurementCanvas: HTMLCanvasElement | undefined;

/**
 * Sets the width of the given element to the width of its text, plus any extra
 * padding specified.
 *
 * @param element HTML element to measure and set.
 * @param extraWidthPx optional extra width to add to the text width, in pixels.
 */
export function adjustWidthToFitTextLength(
  element: HTMLElement,
  extraWidthPx = 0
): void {
  // Credit to https://mcmap.net/q/107711/-adjust-width-of-input-field-to-its-input for this canvas-
  // based approach to calculating text width.
  if (!textMeasurementCanvas) {
    textMeasurementCanvas = document.createElement("canvas");
  }
  const context = textMeasurementCanvas.getContext("2d");
  if (!context) {
    log.error("adjustWidthToFitTextLength() failed to get a canvas context.");
    return;
  }
  const style = window.getComputedStyle(element);
  context.font = style.font;
  let text: string;
  if (element instanceof HTMLInputElement) {
    text = element.value;
  } else if (element instanceof HTMLSelectElement) {
    text = element.options[element.selectedIndex].text;
  } else {
    text = element.innerText;
  }
  const textMetrics = context.measureText(text);
  // CanvasRenderingContext2D.letterSpacing is not supported on Safari, so we
  // calculate the change in width due to letter spacing.
  let totalLetterSpacingPx = 0;
  if (style.letterSpacing && text.length > 1) {
    let letterSpacingPx = 0;
    const match = style.letterSpacing.match(/^(?<val>[0-9.]+)(?<unit>px|ex)$/);
    if (match?.groups?.unit === "px") {
      letterSpacingPx = Number(match.groups?.val);
    } else if (match?.groups?.unit === "ex") {
      const exWidth = context.measureText("x").width;
      letterSpacingPx = Number(match.groups?.val) * exWidth;
    } else {
      log.warn(`Could not parse letter-spacing: "${style.letterSpacing}"`);
    }
    totalLetterSpacingPx = letterSpacingPx * (text.length - 1);
  }
  element.style.width = `${
    textMetrics.width + totalLetterSpacingPx + extraWidthPx
  }px`;
}
Hearken answered 28/3, 2023 at 17:5 Comment(0)
S
-2

Why not using just css?

<div id="wrapper">
  <input onkeyup="keyup(event)">
  <div id="ghost"></div>
</div>

function keyup(e) {
	document.getElementById('ghost').innerText = e.target.value;
}
#wrapper {
  position: relative;
  min-width: 30px;
  display: inline-block;
}

input {
  position: absolute;
  left:0;
  right:0;
  border:1px solid blue;
  width: 100%;
}

#ghost {
  color: transparent;
}
<div id="wrapper">
  <input onkeyup="keyup(event)">
  <div id="ghost"></div>
</div>
wrapper {
  position: relative;
  min-width: 30px;
  border: 1px solid red;
  display: inline-block;
}

input {
  position: absolute;
  left:0;
  right:0;
  width: 100%;
}

#ghost {
  color: transparent;
}

this code was introduced by @Iain Todd to and I thought I should share it

Simmonds answered 21/11, 2017 at 1:30 Comment(2)
It's not just css. It's just another js method. Interesting one.Diphyllous
It contains extra space on the right side, a delay and the first letter makes the input jump to the top.Wiltz

© 2022 - 2024 — McMap. All rights reserved.