Vuetify - How to make setSelectionRange(int, int) work in v-textarea when its model is changed

Viewed 2123

I try to use the method setSelectionRange to change the position of the cursor in a vuetify v-textarea.

It works well when I do not manipulate the data element referred by the v-model attribute of the textarea. However, if I attempt to change the body first, and then apply the setSelectionRange method; the cursor moves directly to the end of the text.

I'm attaching a snippet of a simplified version. Once you hit backspace key on anywhere in the textarea and the cursor should move to index 2; but it moves to the end of the text instead.

If you remove this.body and backspace again, however, it peacefully moves to index 2.

new Vue({
  el: '#app',
  data: {
    body: ''
  },
  created () {
    this.body = 'I am initial body. Hit backspace on somewhere if you want!'
  },
  methods: {
    onBackspaceOrDeleteButtonKeydown (event) {
      // disable default behavior
      event.preventDefault()
      
      let bodyTextArea = this.$refs.pourBody.$el.querySelector('textarea')
      
      // COMMENT OUT THE NEXT LINE TO SEE THE CURSOR MOVES TO INDEX 2 ALWAYS
      this.body = 'I am changed body. My cursor should have moved to index 2 anyways, but it goes to the end like >'
      bodyTextArea.setSelectionRange(2, 2)
    }
  }
})
<!DOCTYPE html>
<html>

<head>
  <link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>

<body>
  <div id="app">
    <v-app>
      <v-content>
        <v-container>
          <v-textarea 
            ref="pourBody"
            outline
            v-model="body"
            auto-grow rows="7"
            @keydown.delete="onBackspaceOrDeleteButtonKeydown"
          ></v-textarea>

        </v-container>
      </v-content>
    </v-app>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
</body>

</html>

The setSelectionRange does not like body change apparently.

How can I work around it?

2 Answers

Wrap setSelectionRange into setTimeout, like so setTimeout(() => bodyTextArea.setSelectionRange(2, 2)). Value is rerendered by v-model after you set cursor to desired position and cursor position is reset. You have to make sure you call setSelectionRange afterwards.

In addition to givehug's solution you can also do

this.$nextTick(() => {
   bodyTextArea.setSelectionRange(2, 2))
})
Related