How to detect Chrome Autofill with Vue.js

Are you having issues with Chrome autofill on your Vue.js forms? The form inputs aren’t saving the values. Or you need to know when someone is using autofill. There’s some things you can do to solve these issues.

Fixing the issue of autofill not saving

At work last week I was running into issues where Chrome autofill was not saving the values that were being autofilled in the input field. The value would show, but it wasn’t actually there. When you clicked inside the input, your cursor would be placed at the beginning of the input. The value from the autofill was also not reflecting in any data property I was using with v-model.

This issue didn’t happen on my home computer so it may just be an issue with the version of Chrome, but I was able to fix it on my work computer. To fix it, you simply need to make sure you add a name attribute to your input field. Once you do that, the issue should be fixed.

Detecting Chrome Autofill

One other issue I ran into was needing to detect when autofill occurred. I was using floating labels, and so when someone was selecting a value from the autofill dropdown, the labels were covering the autofilled value. You can see below what I mean:

There is a pretty clever way to solve this. You can use css animations to detect when an input is autofilled. To preface this, I found out how to solve this from this issue thread on github.

So to start, add the following css to your project:

:-webkit-autofill {
   animation-name: onAutoFillStart;
}

:not(:-webkit-autofill) {
   animation-name: onAutoFillCancel;
}


@keyframes onAutoFillStart {
   from {

   }
   to {

   }
}


@keyframes onAutoFillCancel {
   from {

   }
   to {

   }
}

Here we’re simply creating two empty animations, one that’s used when autofill is triggered (onAutoFillStart) and one when someone leaves the autofill dropdown (onAutoFillCancel). :-webkit-autofill gets triggered on input fields when autofill is happening. So inside that css definition we’re setting it so the start animation occurs. And the :not(:-webkit-autofill) is the opposite so we’re setting the cancel autofill animation.

Next, you need to add a listener on your input field. To do that simply add a @animationstart event listener to your input field like so:

<input type="text" @animationstart="checkAnimation">

Then you need to create a method, here I’m calling it checkAnimation but you can call it whatever you’d like.

Inside the checkAnimation method you get the animation name by checking the animationName property of the event that’s passed to the method. Here’s an example:

checkAnimation(e) {

	if(e.animationName == "onAutoFillStart")
	{
		this.autofilled = true;
	}
	else if(e.animationName == "onAutoFillCancel")
	{
		this.autofilled = false;
	}

}

And that’s it! You should now be able to figure out whether or not autofill is being used. One thing you should know is that the animations need to be globally scoped. If you create them inside a scoped style, the animationName will include the extra data attributes that Vue adds when you scope styles.

If you want to see a full example take a look at my sample project code below:

App.vue

<template>
   <div id="app" class="container">
       <form class="col-6 offset-3">
           <app-input v-model="firstName" label="First Name" />
           <app-input v-model="lastName" label="Last Name" />
           <app-input v-model="email" label="Email"/>
       </form>
   </div>
</template>

<script>
import Input from './Input.vue';
export default {
   components: {
       appInput: Input
   },
   data() {
       return {
           firstName: "",
           lastName: "",
           email: ""
       }
   }
}
</script>

<style>
   @import url('https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css');

   #app {
       padding: 20px 0px;
   }
</style>

Input.vue

<template>
   <div class="form-group" :class="{'has-value': value, 'has-focus': hasFocus || autofilled }">
       <label>{{ label }}</label>
       <input type="text" class="form-control" :value="value" @input="onInput" @focus="hasFocus = true" @blur="hasFocus = false" @animationstart="checkAnimation">
   </div>
</template>

<script>
export default {
   props: [
       'value',
       'label'
   ],
   data() {
       return {
           hasFocus: false,
           autofilled: false
       }
   },
   methods: {
       onInput(e) {
           this.$emit("input", e.target.value);
       },
       checkAnimation(e) {

            if(e.animationName == "onAutoFillStart")
            {
                this.autofilled = true;
            }
            else if(e.animationName == "onAutoFillCancel")
            {
                this.autofilled = false;
            }

       }
   }
}
</script>

<style>
   .form-group {
       position: relative;
   }

   .form-group label {
       position: absolute;
       top: 10px;
       left: 12px;
       pointer-events: none;
       color: #888888;
   }

   .form-group .form-control {
       height: 45px;
   }

   .form-group.has-focus label {
       color: #009fff;
   }

   .form-group.has-focus label,
   .form-group.has-value label {
       font-size: 12px;
       top: 3px;
   }

   .form-group.has-focus .form-control,
   .form-group.has-value .form-control {
       padding-top: 20px;
   }


   :-webkit-autofill {
       animation-name: onAutoFillStart;
   }

   :not(:-webkit-autofill) {
       animation-name: onAutoFillCancel;
   }


   @keyframes onAutoFillStart {
       from {

       }
       to {

       }
   }


   @keyframes onAutoFillCancel {
       from {

       }
       to {

       }
   }

</style>


Hopefully those tips were helpful for you. If you have any questions feel free to leave a comment below!