Is there a way to scale the width of an <input type="text">
to the width of the actual value?
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_
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, ' '));
// 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:
//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_
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.
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(){_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_
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.
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_
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).
// (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_
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');
Source: Stackoverflow.com