5

I have a fairly simple CSS question. I have an input text field, and on page load i would like it to be 150px in width.

However, as the user enters some text, if the text is greater than 150px in width, then the width should auto adjust.

Here's a plunker:

http://plnkr.co/edit/ig0BQrJDiEtXKV8zJ2w2?p=preview

HTML:

<input class="input-class" type="text" placeholder="Placeholder">

CSS:

.input-class-2 {
  -moz-border-bottom-colors: none;
  -moz-border-left-colors: none;
  -moz-border-right-colors: none;
  -moz-border-top-colors: none;
  border-color: -moz-use-text-color -moz-use-text-color #ef8e80;
  border-image: none;
  border-style: none none dashed;
  border-width: 0 0 1px;
  color: #ef8e80;
  cursor: pointer;
  font-family: Gotham-Book;
  font-size: 18px;
  min-width: 150px;
}

I assumed min-width would do this.

Oam Psy
  • 8,555
  • 32
  • 93
  • 157
  • You can watch the keyboard events with javascript and adapt the input size as the user types stuff in. – Morb Apr 22 '15 at 07:49
  • 1
    It'll probably be better to use a library or plugin that auto expands textareas/inputs. Here is a question that will help: http://stackoverflow.com/questions/931207/is-there-a-jquery-autogrow-plugin-for-text-fields – Tony Barnes Apr 22 '15 at 07:50
  • Just like @Morb said I doubt you can accomplish that effect without using `javascript` – Joel Almeida Apr 22 '15 at 07:50
  • 1
    @Akshay - Yes, jQuery is fine to use. – Oam Psy Apr 22 '15 at 07:52

4 Answers4

7

There currently is no way to achieve this with pure CSS, perhaps once calc and attr can be used in combination, but not currently. So we have to fall back to JavaScript.

There isn't any real reason to use jQuery for this. You can argue that your "concerns should be separated" i.e. code should be separate from mark-up, but that is easy to do using addEventListener. However, if I'm dealing with one off small bits of JavaScript it tends to be faster — in terms of implementation, page render and even for those trying to track down what is making the input behave strangely — to use inline event listeners.

<input type="text" 
       style="min-width: 150px;" 
       onkeyup="this.size = Math.max(this.value.length, 1)" 
/>

or:

<input type="text" 
       style="width: 150px;" 
       onkeyup="
         this.style.width = '1px';
         this.style.width = (
             this.scrollWidth &gt; 140
           ? this.scrollWidth + 10
           : 150
         )+'px';
       " 
/>

Disclaimer: Obviously if you are implementing many of these inputs it is far better to code a generalised function to handle them. Plus it is always far better to avoid inline style by using a stylesheet.

/**
 * Directly setting the size attribute, with minWidth
 */
function autosize(elm, minWidth){
  var keyup = function(e){
    var t = e.target || e.srcElement;
    var v = Math.max(t.value.length, 1);
    t.setAttribute
      ? t.setAttribute('size', v)
      : (t['size'] = v)
    ;
  };
  elm.style.minWidth = minWidth+'px';
  elm.addEventListener
    ? elm.addEventListener('keyup', keyup)
    : elm.attachEvent('onkeyup', keyup)
  ;
};

The size attribute is by far the most obvious choice, although you can directly set the width — if you prefer — using scrollWidth.

/**
 * Directly setting width, with minWidth
 */
function autosize(elm, minWidth){
  var keyup = function(e){
    var t = e.target || e.srcElement;
    t.style.width = '1px';
    t.style.width = t.scrollWidth + 'px';
  };
  elm.style.minWidth = minWidth+'px';
  elm.addEventListener
    ? elm.addEventListener('keyup', keyup)
    : elm.attachEvent('onkeyup', keyup)
  ;
};

You can trigger either of these functions by passing your target element in as the first argument. There are a number of ways of finding your element, the easiest and most universal being getElementById. Although you will only be able to find your element if it has been parsed by the browser, so the script tag you use — to run the following code — will either have to be placed below the element in the mark-up i.e. bottom of <body> (preferable), or after waiting for window load, or DOM readiness.

autosize( document.getElementById('myinput'), 150 );

/**
 * Directly setting width, with minWidth
 */
function autosize1(elm, minWidth){
  var keyup = function(e){
    var t = e.target || e.srcElement;
    t.style.width = '1px';
    t.style.width = t.scrollWidth + 'px';
  };
  elm.style.minWidth = minWidth+'px';
  elm.addEventListener
    ? elm.addEventListener('keyup', keyup)
    : elm.attachEvent('onkeyup', keyup)
  ;
};

/**
 * Directly setting the size attribute, with minWidth
 */
function autosize2(elm, minWidth){
  var keyup = function(e){
    var t = e.target || e.srcElement;
    var v = Math.max(t.value.length, 1);
    t.setAttribute
      ? t.setAttribute('size', v)
      : (t['size'] = v)
    ;
  };
  elm.style.minWidth = minWidth+'px';
  elm.addEventListener
    ? elm.addEventListener('keyup', keyup)
    : elm.attachEvent('onkeyup', keyup)
  ;
};

autosize1( document.getElementById('a'), 150 );
autosize2( document.getElementById('b'), 150 );
<p>Each input is using a different implementation:</p>
<input type="text" 
       style="min-width: 150px;" 
       onkeyup="this.size = Math.max(this.value.length, 1)" 
/><br />
<input type="text" 
       style="width: 150px;" 
       onkeyup="
         this.style.width = '1px';
         this.style.width = (
             this.scrollWidth &gt; 140
           ? this.scrollWidth + 10
           : 150
         )+'px';
       " 
/><br />
<input type="text" id="a" /><br />
<input type="text" id="b" /><br />
Pebbl
  • 34,937
  • 6
  • 62
  • 64
  • question why are you using the keyup and keydown inputs instead of oninput? – Persijn Apr 22 '15 at 10:50
  • 1
    @Persijn ~ Good question. Purely for support, `oninput` is supported from IE9 upwards. I still end up having to support IE8 for some of my projects (thankfully no longer IE6). You could quite happily switch to using `oninput` or both really. – Pebbl Apr 22 '15 at 14:05
2

You can try like this:

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

$('input[type="text"]')
    .keyup(resizeInput)
    .each(resizeInput);

JSFIDDLE DEMO

There is one more alternative of using the

<span contenteditable="true">Some Text</span>

instead of using Input tags.

JSFIDDLE DEMO

Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
1

You can try something like this

$('.input-class').keyup(function(){
    var textlength=$('.input-class').val().length
    $(this).width(textlength * 8)
})
.input-class{
  -moz-border-bottom-colors: none;
  -moz-border-left-colors: none;
  -moz-border-right-colors: none;
  -moz-border-top-colors: none;
  border-color: -moz-use-text-color -moz-use-text-color #ef8e80;
  border-image: none;
  border-style: none none dashed;
  border-width: 0 0 1px;
  color: #ef8e80;
  cursor: pointer;
  font-family: Gotham-Book;
  font-size: 18px;
  min-width: 150px;
    width:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<input class="input-class" type="text" placeholder="Placeholder">
Akshay
  • 14,138
  • 5
  • 46
  • 70
  • This solution is ok, save for the fact you've hard-coded the width of the font. This can change across devices, as can font support, so a version that automatically measures the required width would be better. – Pebbl Apr 22 '15 at 09:29
  • Your's isn't that far off though, and for most cases guessing the width of the font as you have done would be sufficient. You could probably improve your version (at least for modern browsers) using this new measure (if you're interested) https://developer.mozilla.org/en/docs/Web/CSS/length#ch – Pebbl Apr 22 '15 at 10:16
  • @Pebbl Thanks i will have a look at it – Akshay Apr 22 '15 at 11:15
1

Tried to use pure JavaScript. I hide a span element that's not shown (visibility:hidden;) to the user. Then I calculate the span elements rendered width, and setting that to the container of the input.
And setting the input to be width:100%; makes it grow to the size of its parent.

var field = document.getElementById("grow");
field.oninput = function() {
  var ruler = document.getElementById("ruler");
  ruler.innerHTML = field.value.replace(/ /g,"&nbsp;");
  var outer = document.getElementById("outer");
  if (ruler.offsetWidth > 100) {
    outer.setAttribute('style', "width:" + (ruler.offsetWidth + 5) + "px;");
  } else {
    outer.setAttribute('style', "");
  }
};
#grow {
  height: 100%;
  width: 100%;
  font-size: inherit;
  font-family: inherit;
}
#outer {
  width: 100px;
  font-size: 1rem;
  font-family: Serif, "Times New Roman", Georgia;
}
.hidden {
  position: absolute;
  display: inline;
  visibility: hidden;
  padding: 0;
  font-size: inherit;
  font-family: inherit;
  white-space: nowrap;
}
<div id="outer">
  <span id="ruler" class="hidden"></span>
  <input id="grow" type="text/plain"/>
</div>
<p>+ Expands</p>
<p>+ shrinks</p>
<p>+ whitespace handling</p>
Persijn
  • 14,624
  • 3
  • 43
  • 72
  • 1
    Nice idea for measuring, however in order for this to work correctly the font and font-size of both the input and the span would have to be identical. It has always been an annoyance to me that you can't apply defaults from one element type to another; so the only way to fix this (because you can't inherit from an element that doesn't support children i.e. an input) is to specify the font and font sizes specifically, with fixed or similar scaled units. You may also want to make the code allow the input to size back down, currently it won't get smaller if you delete characters. – Pebbl Apr 22 '15 at 10:12