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