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
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:
For other form fields:
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
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() != '')
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)
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
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');
}
);
});