Is there a way to scale the width of an <input type="text"> to the width of the actual value?

input {
  display: block;
  margin: 20px;
  width: auto;
}
<input type="text" value="I've had enough of these damn snakes, on this damn plane!" />

<input type="text" value="me too" />

Solution 1

You can do this the easy way by setting the size attribute to the length of the input contents:

function resizeInput() {
    $(this).attr('size', $(this).val().length);
}

$('input[type="text"]')
    // event handler
    .keyup(resizeInput)
    // resize on page load
    .each(resizeInput);

See: http://jsfiddle.net/nrabinowitz/NvynC/

This seems to add some padding on the right that I suspect is browser dependent. If you wanted it to be really tight to the input, you could use a technique like the one I describe in this related answer, using jQuery to calculate the pixel size of your text.

Solution 2

A SIMPLE BUT PIXEL PERFECT SOLUTION

I have seen several ways to do this but calculating the width of fonts isn't always 100% accurate, it's just an estimate.

I managed to create a pixel perfect way of adjusting the input width by having a hidden placeholder to measure from.


jQuery (Recommended)

$(function() {
  $('#hide').text($('#txt').val());
  $('#txt').width($('#hide').width());
}).on('input', function() {
  $('#hide').text($('#txt').val());
  $('#txt').width($('#hide').width());
});
body,
#txt,
#hide {
  font: inherit;
  margin: 0;
  padding: 0;
}

#txt {
  border: none;
  color: #888;
  min-width: 10px;
}

#txt:focus-visible {
  outline: none;
}

#hide {
  display: none;
  white-space: pre;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p>Lorem ipsum
  <span id="hide"></span><input id="txt" type="text" value="type here ..."> egestas arcu.
</p>


Pure JavaScript

I was unable to determine how jQuery calculates the width of hidden elements so a slight tweak to css was required to accomodate this solution.

const hide = document.getElementById('hide');
const txt = document.getElementById('txt');
resize();
txt.addEventListener("input", resize);

function resize() {
  hide.textContent = txt.value;
  txt.style.width = hide.offsetWidth + "px";
}
body,
#txt,
#hide {
  font: inherit;
  margin: 0;
  padding: 0;
}

#txt {
  border: none;
  color: #888;
  min-width: 10px;
}

#txt:focus-visible {
  outline: none;
}

#hide {
  position: absolute;
  height: 0;
  overflow: hidden;
  white-space: pre;
}
<p>Lorem ipsum
  <span id="hide"></span><input id="txt" type="text" value="type here ..."> egestas arcu.
</p>

Solution 3

If for some reason the other solutions don't work for you, you could use a contenteditable-span instead of an input element.

<span contenteditable="true">dummy text</span>

Note that this is more of a hack and has the severe drawback of allowing totally unsanitized HTML input like letting users enter (and paste) linebreaks, links and other HTML.

So you probably shouldn't use this solution unless you're very carefully sanitising the input...

Update: you probably want to use DreamTeK's solution below.

Solution 4

Edit: The plugin now works with trailing whitespace characters. Thanks for pointing it out @JavaSpyder

Since most other answers didn't match what I needed(or simply didn't work at all) I modified Adrian B's answer into a proper jQuery plugin that results in pixel perfect scaling of input without requiring you to change your css or html.

Example:https://jsfiddle.net/587aapc2/

Usage:$("input").autoresize({padding: 20, minWidth: 20, maxWidth: 300});

Plugin:

//JQuery plugin:
$.fn.textWidth = function(_text, _font){//get width of text with font.  usage: $("div").textWidth();
        var fakeEl = $('<span>').hide().appendTo(document.body).text(_text || this.val() || this.text()).css({font: _font || this.css('font'), whiteSpace: "pre"}),
            width = fakeEl.width();
        fakeEl.remove();
        return width;
    };

$.fn.autoresize = function(options){//resizes elements based on content size.  usage: $('input').autoresize({padding:10,minWidth:0,maxWidth:100});
  options = $.extend({padding:10,minWidth:0,maxWidth:10000}, options||{});
  $(this).on('input', function() {
    $(this).css('width', Math.min(options.maxWidth,Math.max(options.minWidth,$(this).textWidth() + options.padding)));
  }).trigger('input');
  return this;
}



//have <input> resize automatically
$("input").autoresize({padding:20,minWidth:40,maxWidth:300});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input value="i magically resize">
<br/><br/>
called with:
$("input").autoresize({padding: 20, minWidth: 40, maxWidth: 300});

Solution 5

I have a jQuery plugin on GitHub: https://github.com/MartinF/jQuery.Autosize.Input

It mirrors the value of the input, calculates the width and uses it for setting the width of the input.

You can see an live example here: http://jsfiddle.net/mJMpw/2175/

Example of how to use it (because some code is needed when posting a jsfiddle link):

<input type="text" value="" placeholder="Autosize" data-autosize-input='{ "space": 40 }' />

input[type="data-autosize-input"] {
  width: 90px;
  min-width: 90px;
  max-width: 300px;
  transition: width 0.25s;    
}

You just use css to set min/max-width and use a transition on the width if you want a nice effect.

You can specify the space / distance to the end as the value in json notation for the data-autosize-input attribute on the input element.

Of course you can also just initialize it using jQuery

$("selector").autosizeInput();

Solution 6

I've found another solution for this problem not involving JS. In HTML I just put something like:

<div>
  <input class="input" value={someValue} />
  <div class="ghost-input">someValue</div>
</div>

All is needed is to set visibility: hidden on ghost-input and width: 100% on the input itself. It works because input scales to the 100% of its container which width is calculated by the browser itself (based on the same text).

If you add some padding and border to the input field you have to adjust your ghost-input class accordingly (or use calc() in input class).

Solution 7

There are already a lot of good answers here. For fun, I implemented this solution below, based on the other answers and my own ideas.

<input class="adjust">

The input element is adjusted pixel accurate and an additional offset can be defined.

function adjust(elements, offset, min, max) {

    // Initialize parameters
    offset = offset || 0;
    min    = min    || 0;
    max    = max    || Infinity;
    elements.each(function() {
        var element = $(this);

        // Add element to measure pixel length of text
        var id = btoa(Math.floor(Math.random() * Math.pow(2, 64)));
        var tag = $('<span id="' + id + '">' + element.val() + '</span>').css({
            'display': 'none',
            'font-family': element.css('font-family'),
            'font-size': element.css('font-size'),
        }).appendTo('body');

        // Adjust element width on keydown
        function update() {

            // Give browser time to add current letter
            setTimeout(function() {

                // Prevent whitespace from being collapsed
                tag.html(element.val().replace(/ /g, '&nbsp'));

                // Clamp length and prevent text from scrolling
                var size = Math.max(min, Math.min(max, tag.width() + offset));
                if (size < max)
                    element.scrollLeft(0);

                // Apply width to element
                element.width(size);
            }, 0);
        };
        update();
        element.keydown(update);
    });
}

// Apply to our element
adjust($('.adjust'), 10, 100, 500);

The adjustment gets smoothed with a CSS transition.

.adjust {
    transition: width .15s;
}

Here is the fiddle. I hope this can help others looking for a clean solution.

Solution 8

Instead of trying to create a div and measure its width, I think it's more reliable to measure the width directly using a canvas element which is more accurate.

function measureTextWidth(txt, font) {
    var element = document.createElement('canvas');
    var context = element.getContext("2d");
    context.font = font;
    return context.measureText(txt).width;
}

Now you can use this to measure what the width of some input element should be at any point in time by doing this:

// assuming inputElement is a reference to an input element (DOM, not jQuery)
var style = window.getComputedStyle(inputElement, null);
var text = inputElement.value || inputElement.placeholder;
var width = measureTextWidth(text, style.font);

This returns a number (possibly floating point). If you want to account for padding you can try this:

  var desiredWidth = (parseInt(style.borderLeftWidth) +
      parseInt(style.paddingLeft) +
      Math.ceil(width) +
      1 + // extra space for cursor
      parseInt(style.paddingRight) +
      parseInt(style.borderRightWidth))
  inputElement.style.width = desiredWidth + "px";

Solution 9

You can solve this problem as here :) http://jsfiddle.net/MqM76/217/

HTML:

<input id="inpt" type="text" />
<div id="inpt-width"></div>

JS:

$.fn.textWidth = function(text, font) {
    if (!$.fn.textWidth.fakeEl) $.fn.textWidth.fakeEl =      $('<span>').hide().appendTo(document.body);
    $.fn.textWidth.fakeEl.text(text || this.val() || this.text()).css('font', font || this.css('font'));
    return $.fn.textWidth.fakeEl.width(); 
};

$('#inpt').on('input', function() {
    var padding = 10; //Works as a minimum width
    var valWidth = ($(this).textWidth() + padding) + 'px';
    $('#'+this.id+'-width').html(valWidth);
    $('#inpt').css('width', valWidth);
}).trigger('input');

Solution 10

Unfortunately the size attribute will not work very well. There will be extra space and too little space sometimes, depending on how the font is set up. (check out the example)

If you want this to work well, try watching for changes on the input, and resize it then. You probably want to set it to the input's scrollWidth. We would need to account for box sizing, too.

In the following example, I'm setting the size of the input to 1 to prevent it from having a scrollWidth that is greater than our initial width (set manually with CSS).

// (no-jquery document.ready)
function onReady(f) {
    "complete" === document.readyState
        ? f() : setTimeout(onReady, 10, f);
}

onReady(function() {
    [].forEach.call(
        document.querySelectorAll("input[type='text'].autoresize"),
        registerInput
    );
});
function registerInput(el) {
    el.size = 1;
    var style = el.currentStyle || window.getComputedStyle(el),
        borderBox = style.boxSizing === "border-box",
        boxSizing = borderBox
            ? parseInt(style.borderRightWidth, 10) +
                parseInt(style.borderLeftWidth, 10)
            : 0;
    if ("onpropertychange" in el) {
         // IE
         el.onpropertychange = adjust;
    } else if ("oninput" in el) {
         el.oninput = adjust;
    }
    adjust();

    function adjust() {

        // reset to smaller size (for if text deleted) 
        el.style.width = "";

        // getting the scrollWidth should trigger a reflow
        // and give you what the width would be in px if 
        // original style, less any box-sizing
        var newWidth = el.scrollWidth + boxSizing;

        // so let's set this to the new width!
        el.style.width = newWidth + "px";
    }
}
* {
  font-family: sans-serif;
}
input.autoresize {
  width: 125px;
  min-width: 125px;
  max-width: 400px;
}
input[type='text'] {
  box-sizing: border-box;
  padding: 4px 8px;
  border-radius: 4px;
  border: 1px solid #ccc;
  margin-bottom: 10px;
}
<label> 
  Resizes:
  <input class="autoresize" placeholder="this will resize" type='text'>
</label>
<br/>
<label>
  Doesn't resize:
<input placeholder="this will not" type='text'>
</label>
<br/>
<label>
  Has extra space to right:
  <input value="123456789" size="9" type="text"/>
</label>

I think this should work in even IE6, but don't take my word for it.

Depending on your use case, you may need to bind the adjust function to other events. E.g. changing an input's value programmatically, or changing the element's style's display property from none (where scrollWidth === 0) to block or inline-block, etc.

Solution 11

try canvas measureText solution

css:

    input{
        min-width:10px!important;
        max-width:99.99%!important;
        transition: width 0.1s;
        border-width:1px;
    }

javascript:

function getWidthOfInput(input){
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    var text = input.value.length ? input.value : input.placeholder;
    var style = window.getComputedStyle(input);
    ctx.lineWidth = 1;
    ctx.font = style.font;
    var text_width = ctx.measureText(text).width;
    return text_width;
}

function resizable (el, factor) {
    function resize() {
        var width = getWidthOfInput(el);
        el.style.width = width + 'px';
    }
    var e = 'keyup,keypress,focus,blur,change'.split(',');
    for (var i in e){
        el.addEventListener(e[i],resize,false);
    }
    resize();
}

$( "input" ).each( function(i){
    resizable(this);
});

Solution 12

My jQuery plugin works for me:

Usage:

    $('form input[type="text"]').autoFit({

    });

Source code of jquery.auto-fit.js:

;
(function ($) {
    var methods = {
        init: function (options) {
            var settings = $.extend(true, {}, $.fn.autoFit.defaults, options);
            var $this = $(this);

            $this.keydown(methods.fit);

            methods.fit.call(this, null);

            return $this;
        },

        fit: function (event) {
            var $this = $(this);

            var val = $this.val().replace(' ', '-');
            var fontSize = $this.css('font-size');
            var padding = $this.outerWidth() - $this.width();
            var contentWidth = $('<span style="font-size: ' + fontSize + '; padding: 0 ' + padding / 2 + 'px; display: inline-block; position: absolute; visibility: hidden;">' + val + '</span>').insertAfter($this).outerWidth();

            $this.width((contentWidth + padding) + 'px');

            return $this;
        }
    };

    $.fn.autoFit = function (options) {
        if (typeof options == 'string' && methods[options] && typeof methods[options] === 'function') {
            return methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof options === 'object' || !options) {
            // Default to 'init'
            return this.each(function (i, element) {
                methods.init.apply(this, [options]);
            });
        } else {
            $.error('Method ' + options + ' does not exist on jquery.auto-fit.');
            return null;
        }
    };

    $.fn.autoFit.defaults = {};

})(this['jQuery']);

Solution 13

Input elements do behave differently from other elements, which would do just about what you want if you give them float: left (see http://jsfiddle.net/hEvYj/5/). I do not think that is possible without calculating it in some way with JavaScript (i.e. add 5px to the width per letter in the box).

Solution 14

User nrabinowitz' solution is working great, but I use the keypress event instead of keyup. That reduces the latency if the user types slowly.

Solution 15

Here is my modification of nrabinowitz' solution. I didn't use the size property, because it's not perfect with proportional fonts as @Mark noted. My solution place an element after your input and gets width counted by browser (using jQuery).

Although I don't test it, I suppose it will work only if all CSS properties affecting font are inherited.

The input width changes on focusout event, which works better for me. But you can use keyup/keypress to change input's width when typing as well.

function resizeInput() {

    //Firstly take the content or placeholder if content is missing.
    var content =
        $(this).val().length > 0 ? $(this).val() : $(this).prop("placeholder");

    //Create testing element with same content as input.
    var widthTester = $("<span>"+content+"</span>").hide();

    //Place testing element into DOM after input (so it inherits same formatting as input does).
    widthTester.insertAfter($(this));

    //Set inputs width; you may want to use outerWidth() or innerWidth()
    //depending whether you want to count padding and border or not.
    $(this).css("width",widthTester.width()+"px");

    //Remove the element from the DOM
    widthTester.remove();
 }

 $('.resizing-input').focusout(resizeInput).each(resizeInput);

Solution 16

Using canvas we could calculate the elements width:

function getTextWidth(text, fontSize, fontName) {
  let canvas = document.createElement('canvas');
  let context = canvas.getContext('2d');
  context.font = fontSize + fontName;
  return context.measureText(text).width;
}

and use it on the chosen event:

function onChange(e) {
  let width = getTextWidth(this.value, $(this).css('font-size'), 
  $(this).css('font-family'));
  $(this.input).css('width', width);
}

Solution 17

I solved width creating canvas and calculating size of it. its important that input value and canvas share same font features (family, size, weight...)

import calculateTextWidth from "calculate-text-width";

/*
 requires two props "value" and "font"
  - defaultFont: normal 500 14px sans-serif 
 */
const defaultText = 'calculate my width'
const textFont = 'normal 500 14px sans-serif'
const calculatedWidth = calculateTextWidth(defaultText, textFont)
console.log(calculatedWidth) // 114.37890625

GitHub: https://github.com/ozluy/calculate-text-width CodeSandbox: https://codesandbox.io/s/calculate-text-width-okr46

Solution 18

You can pass any input element in this function to get the proper width of the element. This width will be as if the input element were a span element with all the properties of the original element. It will take into account the font-family, font-size, and all other font properties that could have affected the total width of the text as well as the horizontal border and padding of the input element. Additionally, it will return the width of the placeholder, if there isn't any value in the input element.

So, this width value will be suitable for setting the width of the input element. And in that case, you may also want to set a minimum width to the element in case it has neither a value nor a placeholder.

Also, this function will behave somewhat similar to the offsetWidth property except that this function will append px to the end of the width value, return the width value even if the input element were hidden by setting its display to none, won't round the value to an integer, and won't consider the width of vertical scrollbars if there were.

function getInputWidth(element) {
    const text = element.value || element.placeholder;
    const elementStyle = window.getComputedStyle(element);
    const fontProperty = elementStyle.font;
    const horizontalBorder = parseFloat(elementStyle.borderLeftWidth) + parseFloat(elementStyle.borderRightWidth);
    const horizontalPadding = parseFloat(elementStyle.paddingLeft) + parseFloat(elementStyle.paddingRight);

    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    context.font = fontProperty;
    const textWidth = context.measureText(text).width;

    const totalWidth = horizontalBorder + horizontalPadding + textWidth + "px";
    return totalWidth;
}