Vue - change input width according to content

Viewed 2834

So I can do this (Pug & CoffeeScript):

input(placeholder="0", v-model.number="order[index]" v-on:change="adjustInput")
...
adjustInput: ->
    event.target.style.width = event.target.value.length + 'ch'

... but it only works if I change the input in the browser, by hand. The input does not change its width if the v-model changes.

How can I make it so that the input width changes even if the change is due to Vue reactivity?

3 Answers

Check this one, but you must check the font-size on input and on fake_div

var app = new Vue({
  el: '#app',
  data() {
  return {
    order: 1, // your value
    fakeDivWidth: 10, // width from start, so input width = 10px
  };
},
watch: {
  order: {  // if value from input changing
    handler(val) {
      this.inputResize();
    },
  },
},
methods: {
  inputResize() {
    setTimeout(() => {
      this.fakeDivWidth = this.$el.querySelector('.fake_div').clientWidth;
    }, 0);
  },
},
})
.fake_div {
  position: absolute;
  left: -100500vw;
  top: -100500vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
    <input placeholder="0" v-model.number="order" v-bind:style="{width: fakeDivWidth + 'px'}" >
<div class="fake_div">{{ order }}</div> // fake_div needed to see this div width
// этот блок фейковый и нужен только для того, что бы наш input становился такого же размера как этот div
  </div>

Try this

input(placeholder="0", v-model.number="order[index]" v-on:change="adjustInput" :style="{width: reactiveWidth}")

// Your secret Vue code
data() {
   return function() {
       reactiveWidth: 100px; // (some default value)
   }
},
methods: {
  adjustInput() {
     this.reactiveWidth = event.target.value.length + 'ch'
  }
},
computed: {
   reactiveWidth() {
     return this.number + 'ch';
  }
}

Since I don't know all parts of the code, you might need to tweak this a bit. With just binding this.number to a order[index] you are not affecting the width of the input in any way. The computed property listens to changes in number

The easiest workaround to me is to replace the input field with a contenteditable span which will wrap around the text:

<script setup>
import {reactive} from 'vue'

const state = reactive({
    input: 'My width will adapt to the text'
})
</script>


<template>
    <span class="input" @input="e => state.input = e.target.innerText" contenteditable>{{state.input}}</span>
</template>

This works like v-model=state.input

Related