243

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!).

Simone
  • 20,302
  • 14
  • 79
  • 103
Undefined
  • 11,234
  • 5
  • 37
  • 62
  • 1
    The checking takes some microseconds whereas the interval would be firing the check every 100 milliseconds or so... how is that gonna affect performance at all? If there existed an event fired by the browser I'm sure they would have used it. – Esailija Jul 29 '12 at 09:24
  • I see what you mean, but it depends on the first part of my question whether JavaScript is even aware a change has just taken place (e.g. before document.ready) – Undefined Jul 29 '12 at 09:26
  • 1
    THE BEST solution for Chrome / WebKit is to use the DOM selector: document.querySelectorAll('input:-webkit-autofill'); after a short delay setTimeout(... code here... 250); – ChrisN Jun 04 '15 at 21:19
  • basically i want to auto login user if it's autofilled, mad annoying ot log back in when it automatically logs out. – Muhammad Umer Nov 22 '16 at 03:17
  • @ChrisN Your comment is what actually got me a (simple) solution. I don't see it as an answer, though! Post it as one and ping me, so I can upvote it in thanks. – Soron Apr 12 '18 at 07:07
  • Why would you need to know if autofill was used? Autofill should be transparent to the website so websites shouldn't behave differently if autofill is used. The response should be the same as if the user entered the info themselves. – orrd Oct 05 '19 at 22:07

41 Answers41

150

The problem is autofill is handled differently by different browsers. Some dispatch the change event, some don't. So it is almost impossible to hook onto an event which is triggered when browser autocompletes an input field.

  • Change event trigger for different browsers:

    • For username/password fields:

      1. Firefox 4, IE 7, and IE 8 don't dispatch the change event.
      2. Safari 5 and Chrome 9 do dispatch the change event.
    • For other form fields:

      1. IE 7 and IE 8 don't dispatch the change event.
      2. Firefox 4 does dispatch the change change event when users select a value from a list of suggestions and tab out of the field.
      3. Chrome 9 does not dispatch the change event.
      4. Safari 5 does dispatch the change event.

You best options are to either disable autocomplete for a form using autocomplete="off" in your form or poll at regular interval to see if its filled.

For your question on whether it is filled on or before document.ready again it varies from browser to browser and even version to version. For username/password fields only when you select a username password field is filled. So altogether you would have a very messy code if you try to attach to any event.

You can have a good read on this HERE

SeinopSys
  • 8,787
  • 10
  • 62
  • 110
afrin216
  • 2,295
  • 1
  • 14
  • 17
  • 42
    Please note there's a difference between autocomplete and autofill. OP is specifically referring to browsers filling in saved login details on page load. – Robbert Jul 30 '13 at 12:45
  • 2
    A usual hack is a mousenter on some important part of the page. The event gets triggered as the user's mouse heads for the button or some such. – Bryce Nov 17 '13 at 08:35
  • 1
    @Bryce Personally I would go with a 'blur' event due to the autofill being something that happens after you would enter the input area. Obviously it's not ideal but as a fallback for the browsers listed above it can be very helpful – JakeJ Jun 27 '14 at 10:07
  • Don't forget the `input` event. That's what Chrome fires on autocomplete (and probably autofill as well, if Chrome has that; I always turn this stuff off). – T.J. Crowder Sep 05 '14 at 07:14
  • See my one line answer if you just want to know if the value in the textbox was filled in by google chrome autocomplete. – ThdK Jun 02 '16 at 09:08
  • This answer looks better: https://stackoverflow.com/questions/11708092/detecting-browser-autofill/48752077#48752077 – Marcus Crisostomo Nov 04 '19 at 22:15
  • I love dead links. – Charles Byrne Jun 02 '20 at 12:28
115

Solution for WebKit browsers

From the MDN docs for the :-webkit-autofill CSS pseudo-class:

The :-webkit-autofill CSS pseudo-class matches when an element has its value autofilled by the browser

We can define a void transition css rule on the desired <input> element once it is :-webkit-autofilled. JS will then be able to hook onto the animationstart event.

Credits to the Klarna UI team. See their nice implementation here:

Lev Kazakov
  • 1,299
  • 1
  • 10
  • 8
  • 2
    this is, by far, the best solution! Kudos to Lev here for this. Thanks for sharing Klarna UI's solution... nothing else has worked for me until I found this. Life Saver! – Braden Rockwell Napier May 26 '17 at 23:44
  • 2
    As well as the CSS rules the keyframes here https://github.com/klarna/ui/blob/v4.10.0/Field/styles.scss#L181-L189 are also needed – user1069816 Jan 07 '18 at 21:50
  • This worked flawless for me! Needs to play around with it a little to figure out the exact approach. Thank you Klarna team – pritesh Jul 23 '18 at 20:52
  • 1
    It's possible it's because this answer was so long ago, but this solution and the code at Klarna didn't work for me, as I had to change the hook to "transitionstart" rather than "animationstart". https://stackoverflow.com/a/41530600 – Jeremy L. Jan 14 '22 at 09:10
  • Needed to add transition timing in css animation-duration: 0.001s - or else the events are not triggered in chrome – etiennejcharles Jan 25 '22 at 21:15
  • This answer is great, but just using `:autofill` was what worked for me. At the time of writing, `:autofill` seems to be supported by all major browsers. See https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill – JCollier Mar 18 '23 at 17:50
  • on chrome 113 I had to add animation-duration for the event to fire – claudio Jun 01 '23 at 16:01
23

This works for me in the latest Firefox, Chrome, and Edge:

$('#email').on('blur input', function() {
    ....
});
James
  • 3,765
  • 4
  • 48
  • 79
23

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

DrNio
  • 1,936
  • 1
  • 19
  • 25
  • Great thinking! – moto Sep 01 '19 at 00:11
  • This wont work on Mozilla as the background color is applied via filter, The background color remains the default rgb(255, 255, 255) – Chanticleer Jan 14 '21 at 11:31
  • Different variation of this that worked for me: `const isAutofilled = window.getComputedStyle(document.querySelector("#cardNumber"), ":-webkit-autofill").getPropertyValue("background-color") !== 'rgb(255, 255, 255)';` – Maksim Dimitrov Jun 12 '23 at 15:25
19

For google chrome autocomplete, this worked for me:

if ($("#textbox").is(":-webkit-autofill")) 
{    
    // the value in the input field of the form was filled in with google chrome autocomplete
}
ThdK
  • 9,916
  • 23
  • 74
  • 101
  • It worked for me as well. I tried hundreds of solutions and no one worked. Thank you very much, save my day and all the times I searched for a solution. – gregoryp Jul 06 '18 at 22:43
  • 5
    this approach trows error: "Syntax error, unrecognized expression: unsupported pseudo: -webkit-autofill" on other browsers(I tried on Firefox) – anvita surapaneni Jan 07 '19 at 21:30
  • 4
    Not working on chrome as well as of 2020, also throws error unrecognized expression: unsupported pseudo: -webkit-autofill – Leo Feb 28 '20 at 10:00
14

Just in case someone is looking for a solution (just as I was today), to listen to a browser autofill change, here's a custom jquery method that I've built, just to simplify the proccess when adding a change listener to an input:

    $.fn.allchange = function (callback) {
        var me = this;
        var last = "";
        var infunc = function () {
            var text = $(me).val();
            if (text != last) {
                last = text;
                callback();
            }
            setTimeout(infunc, 100);
        }
        setTimeout(infunc, 100);
    };

You can call it like this:

$("#myInput").allchange(function () {
    alert("change!");
});
LcSalazar
  • 16,524
  • 3
  • 37
  • 69
  • 1
    For some reason for me, this code is not working on page redirect (when i logout of application and get redirected to login page where autofill happens). Although it does work on page refresh. – VishwaKumar Jun 05 '15 at 06:38
  • 2
    that looks like quite the mobile battery drainer :/ – Stefan Fisk Jan 26 '18 at 15:49
  • @StefanFisk - Not really it doesn't. A simple repeating timer set at the JS level of a browser page is not enough to impact on your battery, as there is so much going on in most web pages.... You really think *google.com* sets no recurrent timers? What a power management flaw it would be if I could program simply with JS a web page that drains a user's battery.... – LcSalazar Jan 26 '18 at 16:45
13

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;
} 
Machavity
  • 30,841
  • 27
  • 92
  • 100
st0495
  • 139
  • 1
  • 2
6

Unfortunately the only reliable way i have found to check this cross browser is to poll the input. To make it responsive also listen to events. Chrome has started hiding auto fill values from javascript which needs a hack.

  • Poll every half to third of a second ( Does not need to be instant in most cases )
  • Trigger the change event using JQuery then do your logic in a function listening to the change event.
  • Add a fix for Chrome hidden autofill password values.

    $(document).ready(function () {
        $('#inputID').change(YOURFUNCTIONNAME);
        $('#inputID').keypress(YOURFUNCTIONNAME);
        $('#inputID').keyup(YOURFUNCTIONNAME);
        $('#inputID').blur(YOURFUNCTIONNAME);
        $('#inputID').focusin(YOURFUNCTIONNAME);
        $('#inputID').focusout(YOURFUNCTIONNAME);
        $('#inputID').on('input', YOURFUNCTIONNAME);
        $('#inputID').on('textInput', YOURFUNCTIONNAME);
        $('#inputID').on('reset', YOURFUNCTIONNAME);
    
        window.setInterval(function() {
            var hasValue = $("#inputID").val().length > 0;//Normal
            if(!hasValue){
                hasValue = $("#inputID:-webkit-autofill").length > 0;//Chrome
            }
    
            if (hasValue) {
                $('#inputID').trigger('change');
            }
        }, 333);
    });
    
Bucket
  • 514
  • 6
  • 10
  • The problem for me was using `attr('value')` instead of `val()`. Unlike `val()`, `attr('value')` does not contain autofilled value (Firefox)! – Melebius Apr 14 '21 at 06:43
6

My solution:

Listen to change events as you would normally, and on the DOM content load, do this:

setTimeout(function() {
    $('input').each(function() {
        var elem = $(this);
        if (elem.val()) elem.change();
    })
}, 250);

This will fire the change events for all the fields that aren't empty before the user had a chance to edit them.

Camilo Martin
  • 37,236
  • 20
  • 111
  • 154
  • Perfect. The first solution I found working with Chrome! – Rid Iculous Apr 02 '15 at 01:47
  • 3
    It's really @RidIculous how simple some solutions can be. – Camilo Martin Apr 02 '15 at 12:35
  • 4
    But elem.val() returns empty string for autofilled password fields in chrome :( – Hemant_Negi Jul 16 '15 at 15:40
  • @Hemant_Negi Doesn't Chrome dispatch the change event anyway? Correct me if I'm wrong. I'm using LastPass and am too lazy to disable it to see if it also works without. – Camilo Martin Jul 26 '15 at 18:20
  • 1
    Yes chrome dispatch change event but, when i try to retrieve the value it returns empty. – Hemant_Negi Jul 27 '15 at 07:50
  • @Hemant_Negi You can't get the password through scripting, as a security measure (against userscripts and XSS I assume). but you can clear it (`elem.val('')`), and see that it's changed with the change event. If you need to see the sumbitted password, why not use a POST form? – Camilo Martin Jul 28 '15 at 15:32
  • @CamiloMartin i know i can do that but my situation is different. im trying to make this kind of input boxes https://dribbble.com/shots/2145451--01-Compact-Login?list=users&offset=0. but for login form chrome autofills the username and password which my javascript can not detect and hence result in a broken ui :( – Hemant_Negi Jul 31 '15 at 06:55
  • @Hemant_Negi I'm not sure if I understood what part are you stuck on, but here's an idea: use the input `pattern` attribute, and the `:valid` pseudo-selector so that you can implement as much as possible in CSS only. – Camilo Martin Aug 01 '15 at 09:31
  • I just had to plug this little code into my $(document)-ready-fn and it all worked - THANK YOU VERY MUCH, that is useful stuff! :-) – MBaas Oct 13 '15 at 16:17
  • I haven't been able to get this to work, which very well may be my fault since I'm a JS novice, but the `:valid` pseudo selector isn't a solution, because it will trigger on a blank field regardless of whether or not there's a pattern. A blank field is blank, not invalid, and therefore considered valid. The only way to force an invalid state is to set the field to required, which is often the case in the common login use-cases, but there are many other form uses where using required isn't viable. – Chase Feb 13 '16 at 09:14
  • @Chase Maybe you can restrict it further with classes. Like, select `.some-class:valid`? – Camilo Martin Feb 13 '16 at 18:26
  • That would indeed specify which input, but still not whether or not it truly has a value. Unless the input is set to required, the `:valid` pseudo selector still won't help, because a blank input is still considered valid. The issue is auto-filled inputs technically have no value, as far as I can tell in my testing. I think this has to do with the value being loaded into the user-agent Shadow DOM when the page is loaded, with no detectable change to the input itself. But I'm not qualified.... This link has more info http://avernet.blogspot.in/2010/11/autocomplete-and-javascript-change.html – Chase Feb 13 '16 at 21:07
  • @Chase It just occurred to me, are you running the code in my answer on document ready? Alternatively, are the forms generated by Javascript? Another option: toggling a class when input is empty/not empty. – Camilo Martin Feb 14 '16 at 05:13
5

There is a new polyfill component to address this issue on github. Have a look at autofill-event. Just need to bower install it and voilà, autofill works as expected.

bower install autofill-event
David
  • 9,635
  • 5
  • 62
  • 68
  • This worked great with jQuery Custom Forms. I just dropped it in and my custom select lists worked great with autofill. – Chris Bloom Jun 09 '14 at 14:13
5

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;
}
zdrsoft
  • 2,417
  • 19
  • 10
5

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.

AmerllicA
  • 29,059
  • 15
  • 130
  • 154
4

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>
Julesezaar
  • 2,658
  • 1
  • 21
  • 21
3

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"))
Pietro Coelho
  • 1,894
  • 1
  • 26
  • 34
3

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!

Adam Šipický
  • 838
  • 7
  • 6
2

I know this is an old thread but I can imagine many comes to find a solution to this here.

To do this, you can check if the input(s) has value(s) with:

$(function() {
    setTimeout(function() {
        if ($("#inputID").val().length > 0) {
            // YOUR CODE
        }
    }, 100);
});

I use this myself to check for values in my login form when it's loaded to enable the submit button. The code is made for jQuery but is easy to change if needed.

S.Lund
  • 47
  • 2
  • 1
    This code works great for me if you update the third line to `($("#inputID:-webkit-autofill").val().length > 0) {` – Hector Jan 26 '16 at 22:54
2

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>
Suresh Pattu
  • 6,083
  • 16
  • 59
  • 91
2

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

Glen Little
  • 6,951
  • 4
  • 46
  • 68
  • Works in Firefox 86, despite the [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/:-webkit-autofill) and [caniuse](https://caniuse.com/mdn-css_selectors_-webkit-autofill) suggesting otherwise. – Mattwmaster58 Jan 16 '21 at 17:36
2

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);
 });
1

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');
});
agrm
  • 3,735
  • 4
  • 26
  • 36
a828h
  • 146
  • 9
1

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!!

On Chrome, Opera, Edge problem was solved quite EZ

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).

The problem was with Firefox

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.

Gawrion
  • 77
  • 1
  • 9
  • in new version of FF they changed the styling of the autofill input. Now they use linear-gradient(rgba(255, 249, 145, 0.5), rgba(255, 249, 145, 0.5)) as a property of background-image. I changed the code. – Gawrion Dec 14 '21 at 22:43
  • On Firefox there isn't a problem for a normal usage, as "change" and "input" events are fired when does a autocomplete. The only real issue if you need to do something ONLY when the autocomplete happens. – Zardoz89 Apr 18 '22 at 07:47
1

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.

0

I used the blur event on the username to check if the pwd field had been auto-filled.

 $('#userNameTextBox').blur(function () {
        if ($('#userNameTextBox').val() == "") {
            $('#userNameTextBox').val("User Name");
        }
        if ($('#passwordTextBox').val() != "") {
            $('#passwordTextBoxClear').hide(); // textbox with "Password" text in it
            $('#passwordTextBox').show();
        }
    });

This works for IE, and should work for all other browsers (I've only checked IE)

  • @ChrisN I did test my solution with IE, browsers handle the javascsript events differently. My solution is simple and easy to read and is for one of the more difficult browsers to code for. – Bridget Arrington Jul 08 '15 at 13:51
  • 1
    I end up with the same solution: `blur` event. I apply the same function for the `change` and `blur` event and it worked great. A simple solution without extra libraries. – Paulo Cheque May 11 '16 at 17:47
0

I had the same problem and I've written this solution.

It starts polling on every input field when the page is loading (I've set 10 seconds but you can tune this value).
After 10 seconds it stop polling on every input field and it starts polling only on the focused input (if one). It stops when you blur the input and again starts if you focus one.

In this way you poll only when really needed and only on a valid input.

// This part of code will detect autofill when the page is loading (username and password inputs for example)
var loading = setInterval(function() {
    $("input").each(function() {
        if ($(this).val() !== $(this).attr("value")) {
            $(this).trigger("change");
        }
    });
}, 100);
// After 10 seconds we are quite sure all the needed inputs are autofilled then we can stop checking them
setTimeout(function() {
    clearInterval(loading);
}, 10000);
// Now we just listen on the focused inputs (because user can select from the autofill dropdown only when the input has focus)
var focused;
$(document)
.on("focus", "input", function() {
    var $this = $(this);
    focused = setInterval(function() {
        if ($this.val() !== $this.attr("value")) {
            $this.trigger("change");
        }
    }, 100);
})
.on("blur", "input", function() {
    clearInterval(focused);
});

It does not work quite well when you have multiple values inserted automatically, but it could be tweaked looking for every input on the current form.

Something like:

// This part of code will detect autofill when the page is loading (username and password inputs for example)
var loading = setInterval(function() {
    $("input").each(function() {
        if ($(this).val() !== $(this).attr("value")) {
            $(this).trigger("change");
        }
    });
}, 100);
// After 10 seconds we are quite sure all the needed inputs are autofilled then we can stop checking them
setTimeout(function() {
    clearInterval(loading);
}, 10000);
// Now we just listen on inputs of the focused form
var focused;
$(document)
.on("focus", "input", function() {
    var $inputs = $(this).parents("form").find("input");
    focused = setInterval(function() {
        $inputs.each(function() {
            if ($(this).val() !== $(this).attr("value")) {
                $(this).trigger("change");
            }
        });
    }, 100);
})
.on("blur", "input", function() {
    clearInterval(focused);
});
Fez Vrasta
  • 14,110
  • 21
  • 98
  • 160
0

If you only want to detect whether auto-fill has been used or not, rather than detecting exactly when and to which field auto-fill has been used, you can simply add a hidden element that will be auto-filled and then check whether this contains any value. I understand that this may not be what many people are interested in. Set the input field with a negative tabIndex and with absolute coordinates well off the screen. It's important that the input is part of the same form as the rest of the input. You must use a name that will be picked up by Auto-fill (ex. "secondname").

var autofilldetect = document.createElement('input');
autofilldetect.style.position = 'absolute';
autofilldetect.style.top = '-100em';
autofilldetect.style.left = '-100em';
autofilldetect.type = 'text';
autofilldetect.name = 'secondname';
autofilldetect.tabIndex = '-1';

Append this input to the form and check its value on form submit.

bornSwift
  • 336
  • 2
  • 8
0

There does appear to be a solution to this that does not rely on polling (at least for Chrome). It is almost as hackish, but I do think is marginally better than global polling.

Consider the following scenario:

  1. User starts to fill out field1

  2. User selects an autocomplete suggestion which autofills field2 and field3

Solution: Register an onblur on all fields that checks for the presence of auto-filled fields via the following jQuery snippet $(':-webkit-autofill')

This won't be immediate since it will be delayed until the user blurs field1 but it doesn't rely on global polling so IMO, it is a better solution.

That said, since hitting the enter key can submit a form, you may also need a corresponding handler for onkeypress.

Alternately, you can use global polling to check $(':-webkit-autofill')

Dan D
  • 149
  • 1
  • 7
0

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();
});
roger
  • 1,225
  • 2
  • 17
  • 33
0

I succeeded on chrome with :

    setTimeout(
       function(){
          $("#input_password").focus();
          $("#input_username").focus();
          console.log($("#input_username").val());
          console.log($("#input_password").val());
       }
    ,500);
Antoine O
  • 171
  • 13
0

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');
          }
      );
  });
Romick
  • 163
  • 1
  • 1
  • 11
  • An explanation for your solution would help – ton Aug 20 '18 at 05:41
  • I thought it was obviously. So basically idea is to check value of the input field couple times. In current implementation it is 10 times with 100 ms delay. So when during a second 10*1000=1sec browser fill the autofill value then the callback passed to autofill will be called. In current example it just adds a class. If you have more questions please do not hesitate and just ask :). – Romick Aug 23 '18 at 08:19
0

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

Cvetan Himchev
  • 123
  • 1
  • 7
0

I had a hard time detecting auto-fill in Firefox. This is the only solution that worked for me:

Demo

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() != '')

Serj
  • 41
  • 1
  • 9
0

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);
      }
     }
Tom Esposito
  • 167
  • 1
  • 6
0
$('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.

David Buck
  • 3,752
  • 35
  • 31
  • 35
Sayuto
  • 27
  • 1
  • 5
0

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);
0

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.

Madian Malfi
  • 595
  • 1
  • 8
  • 26
0

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>
regnete
  • 136
  • 3
0

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.

0

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;
    }
  });
}
RiZKiT
  • 2,107
  • 28
  • 23
0
cardNumberStripeEl.on('change', function (event) {
    const isAutofilled = window.getComputedStyle(document.querySelector("#cardNumber"), ":-webkit-autofill").getPropertyValue("background-color") !== 'rgb(255, 255, 255)';

    setOutcome(event, isAutofilled);
});
Maksim Dimitrov
  • 113
  • 2
  • 11
-1

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
PJ Brunet
  • 3,615
  • 40
  • 37
-3

try in CSS

input:-webkit-autofill { border-color: #9B9FC4 !important; }