19

When trying to create a login form with outlined text fields in Vutify, the chrome autocomplete overlap with labels,

<v-text-field
  v-model="email"
  label="e-mail"
  name="email"
  outlined
  prepend-icon="mdi-account"
  type="text"
  required
>
</v-text-field>

enter image description here

you can regeneare here please fill and submit, then go back.

Kuf
  • 17,318
  • 6
  • 67
  • 91
PRAJIN PRAKASH
  • 1,366
  • 1
  • 15
  • 31

6 Answers6

4

This is how I have fixed this issue.

It seems our main problems are the following:

  • The autofill from Chrome, at loading page, not made interface react, letting the design like in your image.
  • So at the time of injection, we should do a fix ourself but no event, from Chrome, can inform us when login/password are auto-filled.

It's interesting to see any click from the browser window FROM USER automatically inform reactivity and all work fine again but it's not work FROM trigger/dispatch internal way.

So first, we need to find a way to react after login/password autofill. And second, we need to fix ourself the design because only a FROM USER action made the design work fine again.

1. React after autofilling at loading page

I use the first solution:

export default {
  //...
  data() {
    return {
      //...
      autofillFix: false,
    }
  },
  //...
  mounted() {
    this.autoLoginCheckingInterface()
  },
  //...
  autoLoginCheckingInterface() {
    // each 100ms we check if the issue was produced
    let intervalDetectAutofill = setInterval(() => {
      if (
        // we target at least one of the stuff that will be affected by autofill
        // to do our checking
        document.querySelectorAll('input[type="password"]:-webkit-autofill')
          .length > 0
      ) {
        // and we inform the system about the issue if it is produced
        this.autofillFix = true

        // we stop to check if issue was produced
        clearInterval(intervalDetectAutofill)
      }
    }, 100)

    // if after 3s nothing appear, means no autofill was made
    setTimeout(() => {
      if (intervalDetectAutofill) {
        clearInterval(intervalDetectAutofill)
        intervalDetectAutofill = null
      }
    }, 3000)
  },
  //...
}
<!--
we will inject `.autofill-fix` class to be able fix design ourself at time of this bug occur
--> 
<v-text-field
      ...
      :class="{ 'autofill-fix': autofillFix }"
      ...
      label="Email address or username"
      ...
      dense
      outlined
      @focus="autofillFix = false"
/>
<!--
we use @focus to let the normal behavior take again the lead
because we know this USER ACTION will made Chrome work well again
-->
<v-text-field
      ...
      :class="{ 'autofill-fix': autofillFix }"
      ...
      label="Password"
      type="password"
      ...
      dense
      outlined
      @focus="autofillFix = false"
/>

2. Fix ourself the design

We can see what are the change when v-text-field is filled. Without content, we can see this:

before

And after autofilling, we can see this:

after

So from the red part, we can see the following code need to be injected at time of .autofill-fix presence to fix the design in the proper way

.autofill-fix.v-text-field--outlined.v-input--dense .v-label {
  left: -28px!important;
  transform: translateY(-16px) scale(.75);
}

Note: You need change the CSS selector if you not use outlined or dense. Be careful about the specificity of selector https://specificity.keegan.st/. In fact, you need adapt the fixed change to your design

Bruno J. S. Lesieur
  • 3,612
  • 2
  • 21
  • 25
  • I like the "2. Fix ourself the design" solution! Well done. – Zaffer Jun 27 '21 at 21:11
  • Your answer is good in its core, but here are a lot of small errors in it. I would try to edit these for you, but the edit queue is full. It would be great if you could peruse those so that this answer has a proper chance to inform less experienced devs, which it deserves. – Jamie Marshall Feb 25 '22 at 15:38
  • solution 1 works for same issue in vuesax. – Saeid Doroudi Mar 14 '22 at 20:54
2

Another way is to defined like @elazard suggest here an autofill variable like this

data () {
        return {
            login: null,
            password: null,
            autofill: false,
            intervalDetectAutofill: null
        }
    },
<v-text-field
    v-model="password"
    type="password"
    label="Password"
    :placeholder="autofill ? ` ` : null"
/>

With the solution given by @adam-reis, in the mounted() of the login page

mounted () {
        // search for autofill every 100ms
        this.intervalDetectAutofill = setInterval(() => {
            if (document.querySelectorAll("input[type=\"password\"]:-webkit-autofill").length > 0) {
                this.autofill = true
            }
        }, 100)

        // clean interval if needed after 3s
        setTimeout(() => {
            if (this.intervalDetectAutofill) {
                clearInterval(this.intervalDetectAutofill)
                this.intervalDetectAutofill = null
            }
        }, 3000)
    },

And of course setting autofill to false if user input

watch: {
        password () {
            this.autofill = false
        },
        autofill () {
            // clean interval if autofill detected or user input
            if (this.intervalDetectAutofill) {
                clearInterval(this.intervalDetectAutofill)
                this.intervalDetectAutofill = null
            }
        }
    },
Harkness
  • 21
  • 6
2

I believe I've achieved a good result with very generic few lines of coding.

 mounted() {
    setTimeout(() => {
      const els = document.querySelectorAll("input:-webkit-autofill")
      els.forEach((el) => {
        const label = el.parentElement.querySelector("label")
        label.classList.add("v-label--active")
      })
    }, 500)
  },

If the browser autofill the v-text-field, this code will add the "active" class to the Label. The v-text-field behaves have no change.

3rdSenna
  • 344
  • 2
  • 16
1

The autofill feature on browsers usually works by straight away setting the value of the fields in question. In this case, the label of the fields, moves out of the way only when the input field is focused, and stays away when it blurs with a value in the field. In case of autofill, the focus event isn't triggered, so the label stays where it is.

To fix this behaviour, you would have to (or get someone to) make changes in Vuetify.

  • But I need some solution with css/javascript. – PRAJIN PRAKASH Aug 14 '20 at 06:26
  • There's no standard way to intercept how a browser autofills forms for you, so I'm not sure you'd be able to know when to modify the fields. Even if you were able to, since Vuetify depends on the input field being focused/blurred, it could mean that for long forms, your screen may move unintentionally. – Ashfaque Ahmad Bari Aug 14 '20 at 16:02
  • But this seems to be a common problem in web, not specific to vutify. – PRAJIN PRAKASH Aug 15 '20 at 06:47
  • It is indeed, and the reason it is common is because of the inconsistent behaviour across browsers. Some trigger change events, some don't. There's no real way to know when the data was autofilled. – Ashfaque Ahmad Bari Aug 15 '20 at 07:13
  • Can you not just run a quick "focus on input field"? that way the vuetify will figure out if there is text in there... – Zaffer Jun 27 '21 at 20:01
1

ok so what i did is something like this :

on the input

:placeholder="!autofilled ? ' ' : ''"

in the script

data() {
        return {
            form: {
                email: '',
                password: '',
            },
            error: null,
            autofilled: false,
        };
},
watch: {
    'form.email'() {
        this.autofilled = true;
     },
},

What it does : basically setting placeholder to one blank space always "raises" the label. the unfortunate side is that setting it statically will make the label unable to go back down even if you empty the input after it is filled. so what i did is make the placeholder dynamic and only set it as a blank space before any change is made to the input after that placeholder goes back to nothing. it isnt perfect because on initial load before the user has a password saved the labels will be raised but i havent found anything much better than that.

Prafulla Kumar Sahu
  • 9,321
  • 11
  • 68
  • 105
elazard
  • 48
  • 6
-1

You could give your input an id and read the input's value when the component is mounted and if it's anything else than empty then you could set your data value to that value that the input is holding, that way the label will go up as you would expect. Based on your more recent comments it seems like you would also need to wait for the DOM to be updated so the best thing we can do is to do our check with the help of nextTick:

mounted()  {
  this.$nextTick(() => {
     const emailValue = document.getElementById('email').value;
     this.email = emailValue || '';
  });

}
  • Also tried this but it `document.getElementById('email').value` will be empty when mounting, If we put this code insisde set timeout this will works ```mounted() { let vm = this setTimeout(function(){ vm.email = document.getElementById('email').value },300); }``` – PRAJIN PRAKASH Aug 15 '20 at 06:44
  • What you could do is to use nextTick, I'll update my answer ``` mounted() { this.$nextTick(() => { // The whole view is rendered, so I can safely access or query // the DOM. }) } ``` – Rony Vidaur Aug 15 '20 at 15:54
  • this don't works Please try the changes on the code pen and post only if it works. – PRAJIN PRAKASH Aug 17 '20 at 10:19