Search code examples
javascriptvue.jsvuejs2html-input

How to make component appear and set keyboard focus to its input field in iOS Safari?


So I figured out how to make iOS select text by using e.target.setSelectionRange(0, 999):

    onUp(e){
      e.preventDefault() // By default, on mouse up the caret is placed between characters. That has to be negated.
      e.target.setSelectionRange(0, 999) // Set the selection to (what is likely) all the text in the input field.
    }
  <input @mouseup="onUp" v-model="input"/>

https://codepen.io/kslstn/pen/ZvoEQa

However, this only works for trusted events: events that are a direct effect of user input (https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted). I want to show an initially hidden Vue component when the user requests it with a click. When it has appeared, the input field inside that component should get its content selected.

I tried the following:

  1. Do the setSelectionRange when the component appears (on mounted() of the component). Doesn't work, because it is not a trusted event (I think).
  2. Let the click event that summons my component set the selection on the component by finding its ID. It would be an ugly solution as it does not cleanly separate my components. It also does not work, because at the time of the click, the component is not in the DOM and the ID of the input field can't be found.
  3. Adding a setTimeout to solution 2. No success: the text selection is triggered by the untrusted timeout event.

Do I want too much?


Solution

  • You won't be able to setSelectionRange if your DOM element doesn't have focus. In your codepen, it does (and you should probably be using the focus event there rather than mouseup), but when a component is mounted, it doesn't.

    Update: This works generally, but apparently not on IOS. This page says:

    setSelectionRange will not work on iOS if the element doesn’t have focus. Also be advised that anything revolving around giving text inputs focus is highly unreliable on mobile Safari.

    The link there says:

    As of iOS 5, handlers triggered by synthesised click events are allowed to trigger focus on input elements. Try the updated FastClick input focus example.

    So we'll probably have to synthesize a click event on the input to get focus there.

    Update (again): It may be that the best you can do is select text on focus. I've updated my code accordingly. If focus works programmatically, text will be selected when the component appears; otherwise, it will be selected when you tap on the field.

    new Vue({
      el: '#app',
      data: {
        showing: false
      },
      components: {
        autoSelected: {
          data() {
            return {
              text: 'hi there'
            }
          },
          mounted() {
            this.$refs.input.focus();
          },
          methods: {
            selectText(e) {
              e.target.setSelectionRange(0, 9999);
            }
          }
        }
      },
      methods: {
        showIt() {
          this.showing = true;
        }
      }
    });
    <script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
    <div id="app">
      <button @click="showIt">Show it</button>
      <auto-selected v-if="showing" inline-template>
        <input ref="input" v-model="text" @focus="selectText">
      </auto-selected>
    </div>