The problem comes with the unit ch
which takes the height as base. Since not all characters have the same height as width it might end up with a margin on the right (for instance by using i
).
https://css-tricks.com/the-lengths-of-css/
You could solve the font issue by either using a monospace font or a contentEditable
element, since it kinda resizes itself.
p{
border: 1px solid #000;
display: inline-block;
min-width: 150px
}
<p contentEditable = 'true'></p>
Depending on what you want you would have to change the css or script to prevent pasting html or linebreaks.
Another option to keep the integrity of the input
element would be by using css ::after
to set the width for you:
.inputText{
display: inline-block;
}
.inputText > input[type=text]{
font-size: 13px;
font-family: arial;
width: 100%
}
.inputText::after{
background:red;
border: 1px solid black;
content: attr(data-content);
display: block;
font-family: arial;
font-size: 13px;
visibility: hidden
}
<div class = 'inputText' data-content = 'text'>
<input type = 'text' placeholder = 'text1' oninput = 'parentNode.setAttribute("data-content", this.value)' />
</div>
<div class = 'inputText' data-content = 'text'>
<input type = 'text' placeholder = 'text2' oninput = 'parentNode.setAttribute("data-content", this.value)' />
</div>
Update
Created a little plugin for it, which does not handle all cases yet the basic ones. Also, I replaced div
with label
since those are more suitable for input
elements. Theoretically, it could be expanded.
;(function(ns){
"use strict";
//REM: Makes an input-element flexible to its width
function _makeFlex(input){
if(input && input.type){
//REM: Wrapping the input-element with a label-element (makes the most sense on inputs)
_wrapInputInLabel(input);
//REM: Adding a listener to inputs, so that the content of othe ::after can be adjusted
input.addEventListener('input', _onInput, false);
}
};
function _onInput(){
var tInput = this,
tParent = tInput.parentNode;
//REM: Just verifying.. better save than sorry :-)
if(tInput.type && tParent.tagName === 'LABEL' && tParent.getAttribute('data-flex')){
//REM: Here exceptions can be set for different input-types
switch(tInput.type.toLowerCase()){
case 'password':
//REM: This one depends on the browser and/or OS
//https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/password
tParent.setAttribute('data-flex-content', tInput.value.replace(/./g, '•'))
break
default:
tParent.setAttribute('data-flex-content', tInput.value)
}
}
};
//REM: Wraps the input in a label-element
function _wrapInputInLabel(input){
if(input){
var tLabel = (input.parentNode && input.parentNode.tagName === 'LABEL') ? input.parentNode : input.parentNode.appendChild(document.createElement('label'));
tLabel.setAttribute('data-flex', input.getAttribute('data-flex'));
tLabel.appendChild(input);
//REM: Copy the font-styles on the label - can be expanded with more
tLabel.style.fontFamily = window.getComputedStyle(input, null).getPropertyValue('font-family');
tLabel.style.fontSize = window.getComputedStyle(input, null).getPropertyValue('font-size');
if(input.id){
tLabel.setAttribute('for', input.id)
}
}
};
ns.Flex = {
Init: function(){
//REM: Loops through all the marked input elements
for(let tL=document.querySelectorAll('input[data-flex]'), i=0, j=tL.length; i<j; i++){
_makeFlex(tL[i])
}
}
}
})(window.mynamespace=window.mynamespace || {});
;window.onload = function(){
mynamespace.Flex.Init()
};
label[data-flex]{
font-family: arial;
font-size: 13px;
display: inline-block;
min-width: 30px;
position: relative
}
label[data-flex]::after{
background: red;
border: 1px solid black;
content: attr(data-flex-content);
display: block;
visibility: hidden
/*REM: following styles are just for demo purposes */
line-height: 20px;
visibility: visible;
}
/*REM: Has a little slider on the right */
label[data-flex='number']::after{
margin-right: 18px
}
label[data-flex] > input{
font-size: 13px;
font-family: arial;
width: 100%
}
<input type = 'text' placeholder = 'text' data-flex = 'flex' id = 'inText' />
<input type = 'text' placeholder = 'crazy' data-flex = 'flex' id = 'inText2' style = 'font-size: 20px; font-family: comic-sans' />
<input type = 'password' placeholder = 'password' data-flex = 'flex' id = 'inPassword' />
<label>
<input type = 'number' placeholder = 'number' data-flex = 'number' id = 'inNumber' />
</label>
Another update
Was researching and trying a bit further and ran into a plugin that tries to do something alike.
I extracted and changed the part for the input
-elements a bit. For me in Chrome all input types work fine. IE11 requires a polyfill for scrollWidth on input
-elements and Firefox has issues with the type number. Yet I guess from the idea using the clientWidth
and scrollWidth
this is - in theory - the best solution:
//REM: Resizes the Input-Element
function resizeInput(input){
if(input.nodeName.toLowerCase() === 'input'){
var tStyle = getComputedStyle(input),
tOffset = 0;
//REM: Input-Elements with no width are likely not visible
if(input.getBoundingClientRect().width){
//REM: Resetting the width for a correct scroll and client calculation
input.style.width = '0';
//REM: Calculting the offset
switch(tStyle.boxSizing){
case 'padding-box':
tOffset = input.clientWidth;
break
case 'content-box':
tOffset = parseFloat(tStyle.minWidth);
break
case 'border-box':
tOffset = input.offsetWidth;
break
};
//REM: Somehow IE11 does not seem to support scrollWidth properly
//https://github.com/gregwhitworth/scrollWidthPolyfill
var tWidth = Math.max(tOffset, input.scrollWidth - input.clientWidth);
input.style.width = tWidth + "px";
//REM: This is kind of a double-check to backtrack the width by setting an unlikely scrollLeft
for(var i=0; i<10; i++){
input.scrollLeft = 1000000;
if(input.scrollLeft === 0){
break;
};
tWidth += input.scrollLeft;
input.style.width = tWidth + 'px'
}
}
else{
//REM: Input-Element is probably not visible.. what to do? >.<
element.style.width = element.value.length + 'ch'
}
}
};
input{
min-width: 30px
}
<input type = 'text' placeholder = 'text' oninput = 'resizeInput(this)' />
<input type = 'number' placeholder = '99' oninput = 'resizeInput(this)' />
<input type = 'password' placeholder = 'password' oninput = 'resizeInput(this)' />