Reputation: 11431
How do you tell if a browser has auto filled a text-box? Especially with username & password boxes that autofill around page load.
My first question is when does this occur in the page load sequence? Is it before or after document.ready?
Secondly how can I use logic to find out if this occurred? Its not that i want to stop this from occurring, just hook into the event. Preferably something like this:
if (autoFilled == true) {
} else {
}
If possible I would love to see a jsfiddle showing your answer.
Possible duplicates
DOM event for browser password autofill?
Browser Autofill and Javascript triggered events
--Both these questions don't really explain what events are triggered, they just continuously recheck the text-box (not good for performance!).
Upvotes: 253
Views: 274172
Reputation: 1
In 2025:
i created a codesandbox with stenciljs to emulate the autofill with all the solutions from here https://codesandbox.io/p/devbox/stencil-component-custom-autofill-detection-k4474k
I used empty css animations:
@keyframes onAutoFillStart {
from {/**/}
to {/**/}
}
@keyframes onAutoFillCancel {
from {/**/}
to {/**/}
}
To handle the animation:
input:-webkit-autofill {
animation: onAutoFillStart 1s;
/* remove blue chrome background */
-webkit-background-clip: text;
}
input:not(:-webkit-autofill) {
animation: onAutoFillCancel 1s;
}
And detect the css animation in js:
handleAutofill = (event) => {
if (event.animationName === 'onAutoFillStart') {
this.isAutofilled = true;
} else {
this.isAutofilled = false;
}
};
<input
type="search"
name={this.name}
value={this.value}
onAnimationStart={this.handleAutofill}
/>
Thanks for the attention
Upvotes: 0
Reputation: 1
These autofill pseudo classes can be checked with mutation observer as well:
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if ((mutation.target as HTMLInputElement).matches(':autofill') ||
(mutation.target as HTMLInputElement).matches(':-webkit-autofill')) {
// logic here
}
}
})
observer.observe(inputElement, {
attributes: true
})
Upvotes: 0
Reputation: 13
You can use onpageshow event which is Window event to detect values auto-filled.
It can be implemented without setTimeout function.
This event will be called whenever a user loads the page or navigate to the page from another page and also called after onload Window event.
addEventListener("pageshow", (event) => {});
onpageshow = (event) => {
// If this value is not undefined, it is autofilled
const userName = document.getElementById('username').val();
};
Upvotes: 0
Reputation: 31
There is inputType key available in event which we received in callback function. If its browser autofilled this will be blank. So you just need to check if its not pasted and dropped.
Upvotes: 2
Reputation: 183
cardNumberStripeEl.on('change', function (event) {
const isAutofilled = window.getComputedStyle(document.querySelector("#cardNumber"), ":-webkit-autofill").getPropertyValue("background-color") !== 'rgb(255, 255, 255)';
setOutcome(event, isAutofilled);
});
Upvotes: 0
Reputation: 2501
Additionally to the other answers, I like to add this helper function, which prevents script errors and which you can use for example on DOMContentLoaded
or onload
(both together with setTimeout >=500, because there is some delay until the browser does autofill), at onchange
on an input element or onsubmit
on a form:
function isAutofilled(element) {
return [":autofill", ":-webkit-autofill", ":-moz-autofill"].some((selector) => {
try {
return element.matches(selector);
} catch {
return false;
}
});
}
Upvotes: 1
Reputation: 31
Well, If we are talking about a controlled inputs, this could be one of possible solutions:
useEffect(() => {
const inputEl = inputRef.current
inputEl.addEventListener('focusout', () => props.onChange(inputRef.value))
}, [])
Note that this may cause unnecessary rerender. The advantages of this solution are its simplicity and support by all browsers. It seems that the use of this approach in small forms is quite acceptable.
Upvotes: 0
Reputation: 87
I spent few hours resolving the problem of detecting autofill inputs on first page load (without any user action taken) and found ideal solution that works on Chrome, Opera, Edge and on FF too!!
by searching elements with pseudoclass input:-webkit-autofill
and doing desired actions (in my case i was changing input wrapper class to change label positions with float label pattern).
becouse FF does not have such pseudoclass or similar class (as many suggest ":-moz-autofill") that is visible by simply searching DOM. You also can't find the yellow background of input. The only cause is that browser adds this yellow color by changing filter property:
input:-moz-autofill, input:-moz-autofill-preview { filter: grayscale(21%) brightness(88%) contrast(161%) invert(10%) sepia(40%) saturate(206%); }
So in case of Firefox You must first search all inputs and get its computed style and then compare to this filter style hardcoded in browser settings. I really dunno why they didnt use simply background color but that strange filter!? They making lifes harder ;)
Here is my code working like a charm at my website (https://my.oodo.pl/en/modules/register/login.php):
<script type="text/javascript">
/*
* this is my main function
*/
var checkAutoFill = function(){
/*first we detect if we have FF or other browsers*/
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if (!isFirefox) {
$('input:-webkit-autofill').each(function(){
/*here i have code that adds "focused" class to my input wrapper and changes
info instatus div. U can do what u want*/
$(this).closest('.field-wrapper').addClass('focused');
document.getElementById("status").innerHTML = "Your browser autofilled form";
});
}
if (isFirefox) {
$('input').each(function(){
var bckgrnd = window.getComputedStyle(document.getElementById(this.id), null).getPropertyValue("background-image");
if (bckgrnd === 'linear-gradient(rgba(255, 249, 145, 0.5), rgba(255, 249, 145, 0.5))') {
/*if our input has that filter property customized by browserr with yellow background i do as above (change input wrapper class and change status info. U can add your code here)*/
$(this).closest('.field-wrapper').addClass('focused');
document.getElementById("status").innerHTML = "Your Browser autofilled form";
}
})
}
}
/*im runing that function at load time and two times more at 0.5s and 1s delay because not all browsers apply that style imediately (Opera does after ~300ms and so Edge, Chrome is fastest and do it at first function run)*/
checkAutoFill();
setTimeout(function(){
checkAutoFill();
}, 500);
setTimeout(function(){
checkAutoFill();
}, 1000);
})
</script>
I edited above code manually here to throw out some trash not important for You. If its not working for u, than paste into Your IDE and double check the syntax ;) Of course add some debuging alerts or console logs and customize.
Upvotes: 1
Reputation: 136
I found a working solution for angularjs.
The trick is to disable the required-property from the input field when the directive detects that the field was populated by the browser via auto fill.
As the input field is no longer required, the login submit buttons is enabled.
This works even if the user didn't click into the body of the window (see Chrome Autofill/Autocomplete no value for password).
Directive:
angular.module('formtools').directive('autofill', [
'$interval', function ($interval)
{
return {
scope: false,
require: 'autofill',
controller: function AutoFillController(){
this.applied = false;
},
controllerAs: 'autoFill',
link: function (scope, elem, attrs, autofill)
{
var refresh = $interval(function() {
// attention: this needs jquery, jqlite from angular doesn't provide this method
if(elem.is(':-webkit-autofill'))
{
autofill.applied = true;
$interval.cancel(refresh);
}
}, 100, 100);
}
}
}]);
HTML:
<form name="loginform">
<input
type="text"
name="username"
autofill
ng-required="!autoFill.applied">
<input
type="password"
name="password"
autofill
ng-required="!autoFill.applied">
<button ng-disabled="loginform.$invalid">Login</button>
</form>
Upvotes: 0
Reputation: 32472
There is a trick to understand browser fills the input or not (boolean):
const inputEl = inputRef.current; // select the el with any way, here is ReactJs ref
let hasValue;
try {
hasValue = inputRef.current.matches(':autofill');
} catch (err) {
try {
hasValue = inputRef.current.matches(':-webkit-autofill');
} catch (er) {
hasValue = false;
}
}
// hasValue (boolean) is ready
After the last curly brace the hasValue
is ready to use. you are able to detect the browser autofill happened or not.
Upvotes: 6
Reputation: 610
I had this issue when using Instagram autofill for email and phone inputs, tried different solutions but nothing worked, At the end all I had to do to disable the autofill is to have different name attributes for phone and email that did the trick.
Upvotes: 0
Reputation: 34
you can try this to detect and clear out all autofill
var autofillclear = false;
setInterval(function() {
if ($("input:-webkit-autofill") && autofillclear == false) {
$("input:-webkit-autofill").each(function() {
if ($(this).val() != '') {
$(this).val('');
autofillclear = true;
}
});
}
}, 500);
Upvotes: 0
Reputation: 47
$('selector').on('keyup', aFunction);
// If tab is active, auto focus for trigger event keyup, blur, change...
// for inputs has been autofill
$(window).on('load', () => {
if (!document.hidden) {
window.focus();
}
})
This works for me. Tested on Chrome.
Upvotes: 0
Reputation: 41
in 2020, this is what worked for me in chrome:
// wait 0.1 sec to execute action after detecting autofill
// check if input username is autofilled by browser
// enable "login" button for click to submit form
$(window).on("load", function(){
setTimeout(function(){
if ($("#UserName").is("input:-webkit-autofill"))
$("#loginbtn").prop('disabled', false);
}, 100);
});
Upvotes: 2
Reputation: 165
For anyone looking for a 2020 pure JS solution to detect autofill, here ya go.
Please forgive tab errors, can't get this to sit nicely on SO
//Chose the element you want to select - in this case input
var autofill = document.getElementsByTagName('input');
for (var i = 0; i < autofill.length; i++) {
//Wrap this in a try/catch because non webkit browsers will log errors on this pseudo element
try{
if (autofill[i].matches(':-webkit-autofill')) {
//Do whatever you like with each autofilled element
}
}
catch(error){
return(false);
}
}
Upvotes: 0
Reputation: 7128
In Chrome and Edge (2020) checking for :-webkit-autofill
will tell you that the inputs have been filled. However, until the user interacts with the page in some way, your JavaScript cannot get the values in the inputs.
Using $('x').focus()
and $('x').blur()
or triggering a mouse event in code don't help.
See https://stackoverflow.com/a/35783761/32429
Upvotes: 2
Reputation: 41
I had a hard time detecting auto-fill in Firefox. This is the only solution that worked for me:
HTML:
<div class="inputFields">
<div class="f_o">
<div class="field_set">
<label class="phold">User</label>
<input type="tel" class="form_field " autocomplete="off" value="" maxlength="50">
</div>
</div>
<div class="f_o">
<div class="field_set">
<label class="phold">Password</label>
<input type="password" class="form_field " autocomplete="off" value="" maxlength="50">
</div>
</div>
</div>
CSS:
/* Detect autofill for Chrome */
.inputFields input:-webkit-autofill {
animation-name: onAutoFillStart;
transition: background-color 50000s ease-in-out 0s;
}
.inputFields input:not(:-webkit-autofill) {
animation-name: onAutoFillCancel;
}
@keyframes onAutoFillStart {
}
@keyframes onAutoFillCancel {
}
.inputFields {
max-width: 414px;
}
.field_set .phold{
display: inline-block;
position: absolute;
font-size: 14px;
color: #848484;
-webkit-transform: translate3d(0,8px,0);
-ms-transform: translate3d(0,8px,0);
transform: translate3d(0,8px,0);
-webkit-transition: all 200ms ease-out;
transition: all 200ms ease-out;
background-color: transparent;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
margin-left: 8px;
z-index: 1;
left: 0;
pointer-events: none;
}
.field_set .phold_active {
font-size: 12px;
-webkit-transform: translate3d(0,-8px,0);
-ms-transform: translate3d(0,-8px,0);
transform: translate3d(0,-8px,0);
background-color: #FFF;
padding-left: 3px;
padding-right: 3px;
}
.field_set input[type='text'], .field_set select, .field_set input[type='tel'], .field_set input[type='password'] {
height: 36px;
}
.field_set input[type='text'], .field_set input[type='tel'], .field_set input[type='password'], .field_set select, .field_set textarea {
box-sizing: border-box;
width: 100%;
padding: 5px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
border: 1px solid #ababab;
border-radius: 0;
}
.field_set {
margin-bottom: 10px;
position: relative;
}
.inputFields .f_o {
width: 100%;
line-height: 1.42857143;
float: none;
}
JavaScript:
// detect auto-fill when page is loading
$(window).on('load', function() {
// for sign in forms when the user name and password are filled by browser
getAutofill('.inputFields');
});
function getAutofill(parentClass) {
if ($(parentClass + ' .form_field').length > 0) {
var formInput = $(parentClass + ' .form_field');
formInput.each(function(){
// for Chrome: $(this).css('animation-name') == 'onAutoFillStart'
// for Firefox: $(this).val() != ''
if ($(this).css('animation-name') == 'onAutoFillStart' || $(this).val() != '') {
$(this).siblings('.phold').addClass('phold_active');
} else {
$(this).siblings('.phold').removeClass('phold_active');
}
});
}
}
$(document).ready(function(){
$(document).on('click','.phold',function(){
$(this).siblings('input, textarea').focus();
});
$(document).on('focus','.form_field', function(){
$(this).siblings('.phold').addClass('phold_active');
});
// blur for Chrome and change for Firefox
$(document).on('blur change','.form_field', function(){
var $this = $(this);
if ($this.val().length == 0) {
$(this).siblings('.phold').removeClass('phold_active');
} else {
$(this).siblings('.phold').addClass('phold_active');
}
});
// case when form is reloaded due to errors
if ($('.form_field').length > 0) {
var formInput = $('.form_field');
formInput.each(function(){
if ($(this).val() != '') {
$(this).siblings('.phold').addClass('phold_active');
} else {
$(this).siblings('.phold').removeClass('phold_active');
}
});
}
});
For Chrome I use: if ($(this).css('animation-name') == 'onAutoFillStart')
For Firefox: if ($(this).val() != '')
Upvotes: 0
Reputation: 3750
To detect email for example, I tried "on change" and a mutation observer, neither worked. setInterval works well with LinkedIn auto-fill (not revealing all my code, but you get the idea) and it plays nice with the backend if you add extra conditions here to slow down the AJAX. And if there's no change in the form field, like they're not typing to edit their email, the lastEmail prevents pointless AJAX pings.
// lastEmail needs scope outside of setInterval for persistence.
var lastEmail = 'nobody';
window.setInterval(function() { // Auto-fill detection is hard.
var theEmail = $("#email-input").val();
if (
( theEmail.includes("@") ) &&
( theEmail != lastEmail )
) {
lastEmail = theEmail;
// Do some AJAX
}
}, 1000); // Check the field every 1 second
Upvotes: -1
Reputation: 1976
I was reading a lot about this issue and wanted to provide a very quick workaround that helped me.
let style = window.getComputedStyle(document.getElementById('email'))
if (style && style.backgroundColor !== inputBackgroundNormalState) {
this.inputAutofilledByBrowser = true
}
where inputBackgroundNormalState
for my template is 'rgb(255, 255, 255)'.
So basically when browsers apply autocomplete they tend to indicate that the input is autofilled by applying a different (annoying) yellow color on the input.
Edit : this works for every browser
Upvotes: 25
Reputation: 2577
Here is CSS solution taken from Klarna UI team. See their nice implementation here resource
Works fine for me.
input:-webkit-autofill {
animation-name: onAutoFillStart;
transition: background-color 50000s ease-in-out 0s;
}
input:not(:-webkit-autofill) {
animation-name: onAutoFillCancel;
}
Upvotes: 5
Reputation: 3396
I was looking for a similar thing. Chrome only... In my case the wrapper div needed to know if the input field was autofilled. So I could give it extra css just like Chrome does on the input field when it autofills it. By looking at all the answers above my combined solution was the following:
/*
* make a function to use it in multiple places
*/
var checkAutoFill = function(){
$('input:-webkit-autofill').each(function(){
$(this).closest('.input-wrapper').addClass('autofilled');
});
}
/*
* Put it on the 'input' event
* (happens on every change in an input field)
*/
$('html').on('input', function() {
$('.input-wrapper').removeClass('autofilled');
checkAutoFill();
});
/*
* trigger it also inside a timeOut event
* (happens after chrome auto-filled fields on page-load)
*/
setTimeout(function(){
checkAutoFill();
}, 0);
The html for this to work would be
<div class="input-wrapper">
<input type="text" name="firstname">
</div>
Upvotes: 4
Reputation: 149
I also faced the same problem where label did not detect autofill and animation for moving label on filling text was overlapping and this solution worked for me.
input:-webkit-autofill ~ label {
top:-20px;
}
Upvotes: 14
Reputation: 848
This is solution for browsers with webkit render engine. When the form is autofilled, the inputs will get pseudo class :-webkit-autofill- (f.e. input:-webkit-autofill {...}). So this is the identifier what you must check via JavaScript.
Solution with some test form:
<form action="#" method="POST" class="js-filled_check">
<fieldset>
<label for="test_username">Test username:</label>
<input type="text" id="test_username" name="test_username" value="">
<label for="test_password">Test password:</label>
<input type="password" id="test_password" name="test_password" value="">
<button type="submit" name="test_submit">Test submit</button>
</fieldset>
</form>
And javascript:
$(document).ready(function() {
setTimeout(function() {
$(".js-filled_check input:not([type=submit])").each(function (i, element) {
var el = $(this),
autofilled = (el.is("*:-webkit-autofill")) ? el.addClass('auto_filled') : false;
console.log("element: " + el.attr("id") + " // " + "autofilled: " + (el.is("*:-webkit-autofill")));
});
}, 200);
});
Problem when the page loads is get password value, even length. This is because browser's security. Also the timeout, it's because browser will fill form after some time sequence.
This code will add class auto_filled to filled inputs. Also, I tried to check input type password value, or length, but it's worked just after some event on the page happened. So I tried trigger some event, but without success. For now this is my solution. Enjoy!
Upvotes: 3
Reputation: 146
I used this solution for same problem.
HTML code should change to this:
<input type="text" name="username" />
<input type="text" name="password" id="txt_password" />
and jQuery code should be in document.ready
:
$('#txt_password').focus(function(){
$(this).attr('type','password');
});
Upvotes: 1
Reputation: 2072
On chrome, you can detect autofill fields by settings a special css rule for autofilled elements, and then checking with javascript if the element has that rule applied.
Example:
CSS
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 30px white inset;
}
JavaScript
let css = $("#selector").css("box-shadow")
if (css.match(/inset/))
console.log("autofilled:", $("#selector"))
Upvotes: 3
Reputation: 123
After research the issue is that webkit browsers does not fire change event on autocomplete. My solution was to get the autofill class that webkit adds and trigger change event by myself.
setTimeout(function() {
if($('input:-webkit-autofill').length > 0) {
//do some stuff
}
},300)
Here is a link for the issue in chromium. https://bugs.chromium.org/p/chromium/issues/detail?id=636425
Upvotes: 0
Reputation: 6209
I have perfect solution for this question try this code snippet.
Demo is here
function ModernForm() {
var modernInputElement = $('.js_modern_input');
function recheckAllInput() {
modernInputElement.each(function() {
if ($(this).val() !== '') {
$(this).parent().find('label').addClass('focus');
}
});
}
modernInputElement.on('click', function() {
$(this).parent().find('label').addClass('focus');
});
modernInputElement.on('blur', function() {
if ($(this).val() === '') {
$(this).parent().find('label').removeClass('focus');
} else {
recheckAllInput();
}
});
}
ModernForm();
.form_sec {
padding: 30px;
}
.form_sec .form_input_wrap {
position: relative;
}
.form_sec .form_input_wrap label {
position: absolute;
top: 25px;
left: 15px;
font-size: 16px;
font-weight: 600;
z-index: 1;
color: #333;
-webkit-transition: all ease-in-out 0.35s;
-moz-transition: all ease-in-out 0.35s;
-ms-transition: all ease-in-out 0.35s;
-o-transition: all ease-in-out 0.35s;
transition: all ease-in-out 0.35s;
}
.form_sec .form_input_wrap label.focus {
top: 5px;
color: #a7a9ab;
font-weight: 300;
-webkit-transition: all ease-in-out 0.35s;
-moz-transition: all ease-in-out 0.35s;
-ms-transition: all ease-in-out 0.35s;
-o-transition: all ease-in-out 0.35s;
transition: all ease-in-out 0.35s;
}
.form_sec .form_input {
width: 100%;
font-size: 16px;
font-weight: 600;
color: #333;
border: none;
border-bottom: 2px solid #d3d4d5;
padding: 30px 0 5px 0;
outline: none;
}
.form_sec .form_input.err {
border-bottom-color: #888;
}
.form_sec .cta_login {
border: 1px solid #ec1940;
border-radius: 2px;
background-color: #ec1940;
font-size: 14px;
font-weight: 500;
text-align: center;
color: #ffffff;
padding: 15px 40px;
margin-top: 30px;
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<form class="form_sec">
<div class="row clearfix">
<div class="form-group col-lg-6 col-md-6 form_input_wrap">
<label>
Full Name
</label>
<input type="text" name="name" id="name" class="form_input js_modern_input">
</div>
</div>
<div class="row clearfix">
<div class="form-group form_input_wrap col-lg-6 col-md-6">
<label>
Emaill
</label>
<input type="email" name="email" class="form_input js_modern_input">
</div>
</div>
<div class="row clearfix">
<div class="form-group form_input_wrap col-lg-12 col-md-12">
<label>
Address Line 1
</label>
<input type="text" name="address" class="form_input js_modern_input">
</div>
</div>
<div class="row clearfix">
<div class="form-group col-lg-6 col-md-6 form_input_wrap">
<label>
City
</label>
<input type="text" name="city" class="form_input js_modern_input">
</div>
<div class="form-group col-lg-6 col-md-6 form_input_wrap">
<label>
State
</label>
<input type="text" name="state" class="form_input js_modern_input">
</div>
</div>
<div class="row clearfix">
<div class="form-group col-lg-6 col-md-6 form_input_wrap">
<label>
Country
</label>
<input type="text" name="country" class="form_input js_modern_input">
</div>
<div class="form-group col-lg-4 col-md-4 form_input_wrap">
<label>
Pin
</label>
<input type="text" name="pincode" class="form_input js_modern_input">
</div>
</div>
<div class="row cta_sec">
<div class="col-lg-12">
<button type="submit" class="cta_login">Submit</button>
</div>
</div>
</form>
Upvotes: 2
Reputation: 163
My solution is:
$.fn.onAutoFillEvent = function (callback) {
var el = $(this),
lastText = "",
maxCheckCount = 10,
checkCount = 0;
(function infunc() {
var text = el.val();
if (text != lastText) {
lastText = text;
callback(el);
}
if (checkCount > maxCheckCount) {
return false;
}
checkCount++;
setTimeout(infunc, 100);
}());
};
$(".group > input").each(function (i, element) {
var el = $(element);
el.onAutoFillEvent(
function () {
el.addClass('used');
}
);
});
Upvotes: 0
Reputation: 171
I succeeded on chrome with :
setTimeout(
function(){
$("#input_password").focus();
$("#input_username").focus();
console.log($("#input_username").val());
console.log($("#input_password").val());
}
,500);
Upvotes: 0
Reputation: 1263
From my personal experience, the below code works well with firefox IE and safari, but isn't working very well at picking autocomplete in chrome.
function check(){
clearTimeout(timeObj);
timeObj = setTimeout(function(){
if($('#email').val()){
//do something
}
},1500);
}
$('#email').bind('focus change blur',function(){
check();
});
Below code works better, because it will trigger each time when user clicks on the input field and from there you can check either the input field is empty or not.
$('#email').bind('click', function(){
check();
});
Upvotes: 0