Home > Mobile >  (Vue3) Problems syncing DOM element heights change using v-model and v-on
(Vue3) Problems syncing DOM element heights change using v-model and v-on

Time:01-13

I am creating a website in which the value of a range slider is bound using v-model, to a property called rangeValue. The code looks similar to the following:

    <template>
       ...
      <input
        type="range"
        min="10"
        max="50"
        
        v-model="rangeValue"
        @change="updateHeight()"
      />
      <div  :style="{ height: `${wrapperHeight}px`></>
      <div
        
        ref="first"
        :style="{ width: `${rangeValue}rem` }">
           <!-- There are elements inside whose height is also bound by rangeValue, and some that are toggled on/off via other checkbox inputs, thereby changing the height of firstDiv -->
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          wrapperHeight: 0,
          rangeValue: "10",
      },
      mounted(){
          this.wrapperHeight = this.$refs.first.offsetHeight;
      },
      methods: {
        updateHeight() {
          this.wrapperHeight = this.$refs.first.offsetHeight;
        }
      }
    }
    </script>
    <style>
      ...
    </style>

I would like to make the height of otherDiv match the height of firstDiv, whose height changes based on range slider changes. My problem is that the height of firstDiv updates concurrently with the range slider changes, while the height of otherDiv always lags one "tick" behind, updating to the value of the firstDiv before it's been changed by the range slider, rather than after. I have tried all sorts of implementations using nextTick to no avail.

In short, I would need to:

  1. Update range slider
  2. Resize firstDiv, get its height in the DOM, resize otherDiv
  3. Repaint DOM (so both are resized at the same time).

I hope this makes sense. Any pointers are more than welcome, thank you!

CodePudding user response:

Is this what you want to achieve?

const { createApp, toRefs, reactive, computed } = Vue

createApp({
  setup: () =>
    toRefs(
      reactive({
        h8: 135
      })
    )
}).mount("#app")
#app .grid {
  display: grid;
  grid-gap: 0.75rem;
  grid-template-columns: repeat(2, 1fr);
  padding: 0.5rem;
  
}
#app .grid > * {
  overflow-y: auto;
  padding: 0.5rem;
  border-radius: 5px;
  box-shadow:
    inset 0 1px 8px 0 rgb(0 0 0/10%),
    inset 0 3px 4px 0 rgb(0 0 0 / 7%),
    inset 0 3px 3px -2px rgb(0 0 0 / 6%);
  background-color: #f8f8f8;
}
#app [type="range"] {
  max-width: 400px;
  width: 100%;
  margin: 0 auto;
  display: block;
}
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>
<div id="app">
  <input type="range" min="60" max="400" v-model="h8" />
  <div  :style="{ height: `${h8}px` }">
    <div >first div</div>
    <div >
      second div
      <p v-for="n in 10" :key="n">Lorem ipsum dolor sit amet.</p>
    </div>
  </div>
</div>

CodePudding user response:

I don't really get your goal and height/width relation.

But here is a working playground with something alike.

const { createApp, ref, onMounted } = Vue;

const App = {
  setup() {    
    const mydiv = ref(null);     
    const wrapperHeight = ref(0);        
    const rangeValue = ref(50);        
    
    onMounted(() => { 
      console.log(`onMounted(): ${mydiv.value.offsetHeight}`);
      wrapperHeight.value = mydiv.value.offsetHeight;
    });        
    
    const updateHeight = (event) => { 
      console.log(`updateHeight(): ${event.target.value}`);
      wrapperHeight.value = event.target.value;
    }
    
    return {mydiv, wrapperHeight, rangeValue, updateHeight }
  }
}

const app = createApp(App)
app.mount('#app')
<div id="app"> 
   <input
        type="range"
        min="10"
        max="100"
        
        v-model="rangeValue"
        @change="updateHeight"
      /><br/>
   rangeValue: {{rangeValue}} <br/>
   wrapperHeight: {{wrapperHeight}} <br/><br/>
   <div :style="{ 'background-color': 'yellow', 'min-height': `${wrapperHeight}px` }">
      <div ref="mydiv" style="{ 'float': 'bottom' }">My Div</div>
   </div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

  • Related