Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Input width fit to content

Tags:

So I'm running into a bit of a problem.

I've been trying to find a way to make the width of an input element fit the content. I kept making progress but then for whatever reason that method didn't work. So now I'm on here looking for help. I'll share the methods that I've attempted that didn't end up working for me.

note: For all of these attempts, I use ctx.measureText(text) which helps by getting the width of the text

another note: It might not be possible to fix, and if it's not that's okay, but for all of these methods, the width doesn't update until the end if you hold down a key and it repeats

Method 1: keyup event

var ctx = document.querySelector('canvas').getContext('2d');
var input = document.querySelector('input');
ctx.font = window.getComputedStyle(input).getPropertyValue('font-size')+' '+window.getComputedStyle(input).getPropertyValue('font-family');

input.addEventListener('keyup', () => {
  input.style.width = `${ctx.measureText(input.value).width+3}px`;
});
<input placeholder="Placeholder"></input>
<canvas style="display: none;"></canvas>

So far, this is the best method in my opinion. It works pretty well, however, since it fires on the keyup event, it doesn't change the width until after the character has been typed, which creates a jittery effect.

Method 2: keyup event plus padding

var ctx = document.querySelector('canvas').getContext('2d');
var input = document.querySelector('input');
ctx.font = window.getComputedStyle(input).getPropertyValue('font-size')+' '+window.getComputedStyle(input).getPropertyValue('font-family');

input.addEventListener('keyup', () => {
  input.style.width = `${ctx.measureText(input.value+'W').width+3}px`;
});
<input placeholder="Placeholder"></input>
<canvas style="display: none;"></canvas>

This method is the same as the previous one, but I've adding a padding of the width of the character 'W' (because it's the widest letter in almost every font). This removes the jittery effect, but then there is an unnecessary amount of space left when you're done.

I tried doing this in addition to changing it to the proper width on the blur event, but this caused a major (unrelated) bug in my program.

You could also use the change event, but if somebody presses a key that doesn't type out a character, such as Alt, it adds the padding, then since there wasn't a change the padding stays (You could disable the event listener while the element is focused but that is very inefficient and annoying).

You could also only add to the padding only if a character has been added, but the only two ways I can think of to do that are, again, very inefficient and annoying. (either keeping a variable for the last length of the input and seeing if it changed, or using String.fromCharCode(event.keyCode) is greater than 0)

Method 3: keydown with padding

var ctx = document.querySelector('canvas').getContext('2d');
var input = document.querySelector('input');
ctx.font = window.getComputedStyle(input).getPropertyValue('font-size')+' '+window.getComputedStyle(input).getPropertyValue('font-family');

input.addEventListener('keydown', () => {
  input.style.width = `${ctx.measureText(input.value+'W').width+3}px`;
});
<input placeholder="Placeholder"></input>
<canvas style="display: none;"></canvas>

This is slightly better than the method before, but the same problems apply.

Also, using keypress gives you the same result as keydown. I could provide every example that I've tried, but they're mostly slightly different variations of everything above, so I'll let you guys be creative and try to think of a solution.

I'm open to using jQuery if that's the only way, but I would prefer otherwise.

Thank you!

like image 527
Andrew Avatar asked Jul 01 '18 22:07

Andrew


1 Answers

I would consider another trick. You can use an editable div on where you apply some properties to allow it to fit its content (basically make it an inline element), then with JS you get the text and you put it on the input.

Here is a simple example. You only have to deal with the initial width and set it like you want. You can also easily add a placeholder and remove it using JS.

document.querySelector(".input").addEventListener("input", function(e) {
    document.querySelector("input").value=e.target.innerHTML;
}, false);
.input {
  border:1px solid #000;
  display:inline-flex;
  min-width:20px;
}
<div class="input" contenteditable="true">
</div>
<input type="hidden">
like image 168
Temani Afif Avatar answered Oct 25 '22 00:10

Temani Afif