[jquery] Auto-scaling input[type=text] to width of value?

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

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

This question is related to jquery html css

The answer is


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).


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


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.


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.


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);
}

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);

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:

_x000D_
_x000D_
//JQuery plugin:_x000D_
$.fn.textWidth = function(_text, _font){//get width of text with font.  usage: $("div").textWidth();_x000D_
        var fakeEl = $('<span>').hide().appendTo(document.body).text(_text || this.val() || this.text()).css({font: _font || this.css('font'), whiteSpace: "pre"}),_x000D_
            width = fakeEl.width();_x000D_
        fakeEl.remove();_x000D_
        return width;_x000D_
    };_x000D_
_x000D_
$.fn.autoresize = function(options){//resizes elements based on content size.  usage: $('input').autoresize({padding:10,minWidth:0,maxWidth:100});_x000D_
  options = $.extend({padding:10,minWidth:0,maxWidth:10000}, options||{});_x000D_
  $(this).on('input', function() {_x000D_
    $(this).css('width', Math.min(options.maxWidth,Math.max(options.minWidth,$(this).textWidth() + options.padding)));_x000D_
  }).trigger('input');_x000D_
  return this;_x000D_
}_x000D_
_x000D_
_x000D_
_x000D_
//have <input> resize automatically_x000D_
$("input").autoresize({padding:20,minWidth:40,maxWidth:300});
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<input value="i magically resize">_x000D_
<br/><br/>_x000D_
called with:_x000D_
$("input").autoresize({padding: 20, minWidth: 40, maxWidth: 300});
_x000D_
_x000D_
_x000D_


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();

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);
});

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


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)

_x000D_
_x000D_
$(function(){_x000D_
  $('#hide').text($('#txt').val());_x000D_
  $('#txt').width($('#hide').width());_x000D_
}).on('input', function () {_x000D_
  $('#hide').text($('#txt').val());_x000D_
  $('#txt').width($('#hide').width());_x000D_
});
_x000D_
body,_x000D_
#txt,_x000D_
#hide{_x000D_
  font:inherit;_x000D_
  margin:0;_x000D_
  padding:0;_x000D_
}_x000D_
#txt{_x000D_
  border:none;_x000D_
  color:#888;_x000D_
  min-width:10px;_x000D_
}_x000D_
#hide{_x000D_
  display:none;_x000D_
  white-space:pre;_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
_x000D_
<p>Lorem ipsum _x000D_
  <span id="hide"></span><input id="txt" type="text" value="type here ...">_x000D_
  egestas arcu._x000D_
</p>
_x000D_
_x000D_
_x000D_


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.

_x000D_
_x000D_
var hide = document.getElementById('hide');_x000D_
var txt = document.getElementById('txt');_x000D_
resize();_x000D_
txt.addEventListener("input", resize);_x000D_
_x000D_
function resize() {_x000D_
  hide.textContent = txt.value;_x000D_
  txt.style.width = hide.offsetWidth + "px";_x000D_
}
_x000D_
body,_x000D_
#txt,_x000D_
#hide {_x000D_
  font: inherit;_x000D_
  margin: 0;_x000D_
  padding: 0;_x000D_
}_x000D_
_x000D_
#txt {_x000D_
  border: none;_x000D_
  color: #888;_x000D_
  min-width: 10px;_x000D_
}_x000D_
_x000D_
#hide {_x000D_
  position: absolute;_x000D_
  height: 0;_x000D_
  overflow: hidden;_x000D_
  white-space: pre;_x000D_
}
_x000D_
<p>Lorem ipsum_x000D_
  <span id="hide"></span><input id="txt" type="text" value="type here ..."> egestas arcu._x000D_
</p>
_x000D_
_x000D_
_x000D_


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']);

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.


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).

_x000D_
_x000D_
// (no-jquery document.ready)_x000D_
function onReady(f) {_x000D_
    "complete" === document.readyState_x000D_
        ? f() : setTimeout(onReady, 10, f);_x000D_
}_x000D_
_x000D_
onReady(function() {_x000D_
    [].forEach.call(_x000D_
        document.querySelectorAll("input[type='text'].autoresize"),_x000D_
        registerInput_x000D_
    );_x000D_
});_x000D_
function registerInput(el) {_x000D_
    el.size = 1;_x000D_
    var style = el.currentStyle || window.getComputedStyle(el),_x000D_
        borderBox = style.boxSizing === "border-box",_x000D_
        boxSizing = borderBox_x000D_
            ? parseInt(style.borderRightWidth, 10) +_x000D_
                parseInt(style.borderLeftWidth, 10)_x000D_
            : 0;_x000D_
    if ("onpropertychange" in el) {_x000D_
         // IE_x000D_
         el.onpropertychange = adjust;_x000D_
    } else if ("oninput" in el) {_x000D_
         el.oninput = adjust;_x000D_
    }_x000D_
    adjust();_x000D_
_x000D_
    function adjust() {_x000D_
_x000D_
        // reset to smaller size (for if text deleted) _x000D_
        el.style.width = "";_x000D_
_x000D_
        // getting the scrollWidth should trigger a reflow_x000D_
        // and give you what the width would be in px if _x000D_
        // original style, less any box-sizing_x000D_
        var newWidth = el.scrollWidth + boxSizing;_x000D_
_x000D_
        // so let's set this to the new width!_x000D_
        el.style.width = newWidth + "px";_x000D_
    }_x000D_
}
_x000D_
* {_x000D_
  font-family: sans-serif;_x000D_
}_x000D_
input.autoresize {_x000D_
  width: 125px;_x000D_
  min-width: 125px;_x000D_
  max-width: 400px;_x000D_
}_x000D_
input[type='text'] {_x000D_
  box-sizing: border-box;_x000D_
  padding: 4px 8px;_x000D_
  border-radius: 4px;_x000D_
  border: 1px solid #ccc;_x000D_
  margin-bottom: 10px;_x000D_
}
_x000D_
<label> _x000D_
  Resizes:_x000D_
  <input class="autoresize" placeholder="this will resize" type='text'>_x000D_
</label>_x000D_
<br/>_x000D_
<label>_x000D_
  Doesn't resize:_x000D_
<input placeholder="this will not" type='text'>_x000D_
</label>_x000D_
<br/>_x000D_
<label>_x000D_
  Has extra space to right:_x000D_
  <input value="123456789" size="9" type="text"/>_x000D_
</label>
_x000D_
_x000D_
_x000D_

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.


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).


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";

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');

Examples related to jquery

How to make a variable accessible outside a function? Jquery assiging class to th in a table Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Getting all files in directory with ajax Bootstrap 4 multiselect dropdown Cross-Origin Read Blocking (CORB) bootstrap 4 file input doesn't show the file name Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource how to remove json object key and value.?

Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to css

need to add a class to an element Using Lato fonts in my css (@font-face) Please help me convert this script to a simple image slider Why there is this "clear" class before footer? How to set width of mat-table column in angular? Center content vertically on Vuetify bootstrap 4 file input doesn't show the file name Bootstrap 4: responsive sidebar menu to top navbar Stylesheet not loaded because of MIME-type Force flex item to span full row width