2019, Chrome 76, approach to autocomplete off - forms

There are are few posts out there about this. You spend hours going through each answer, testing, reading comments, to find that there is no solution. What have you done in 2019, Chrome 76, that works?

Update, January 2020: It appears that as of Chrome 79, Autocomplete (as defined here) no longer treats autocomplete="some-unrecognised-value" as equal to autocomplete="on", so autocomplete="nope" or similar is now effective at disabling both Autocomplete and Autofill.
Update, April 2020: They changed it again. As of Chrome 81, autocomplete="some-unrecognised-value" is no longer effective at disabling the Autocomplete mechanism. However, Autofill now seems to be a lot more conservative than it was before - it still doesn't follow the spec (a field with name="email" and autocomplete="off" will still receive Autofill suggestions) but it doesn't offer up spurious address fragments on random form fields. My recommendation right now would therefore be to use autocomplete="off". If you want to do that on a field named email, you're probably out of luck though :-(
TL,DR: There appears to be no setting for the autocomplete attribute that will reliably turn off all autocomplete dropdowns. However, the circumstances that have led to this are quite convoluted and worth documenting, to hopefully clear up the masses of conflicting advice...
There are two distinct mechanisms present in current (76.0.3809.132) versions of Chrome, which we'll refer to as Autofill and Autocomplete (not necessarily their official names):
Autofill
The Autofill feature attempts to fill in forms using the address information stored in your browser settings. It can be identified by the "Manage addresses..." option (or similar) at the bottom of the dropdown. This feature does not honour autocomplete="off" or autocomplete="false", as a deliberate decision on the part of the Chrome developers.
In a statement outlining this decision, zkoch offered this workaround:
In cases where you really want to disable autofill, our suggestion at
this point is to utilize the autocomplete attribute to give valid,
semantic meaning to your fields. If we encounter an autocomplete
attribute that we don't recognize, we won't try and fill it.
As an example, if you have an address input field in your CRM tool
that you don't want Chrome to Autofill, you can give it semantic
meaning that makes sense relative to what you're asking for: e.g.
autocomplete="new-user-street-address". If Chrome encounters that, it
won't try and autofill the field.
This is the basis of attempted solutions such as autocomplete="nope"; the Autofill mechanism will skip any fields with autocomplete attribute values it doesn't recognise.
The code that implements this decision, for the record: https://chromium.googlesource.com/chromium/src/+/refs/tags/78.0.3903.1/components/autofill/core/browser/form_structure.cc#1218
Autocomplete
The Autocomplete feature provides a dropdown of previously-submitted values from this form field. This dropdown does not have a "Manage addresses..." option. Autocomplete does honour the autocomplete="off" or autocomplete="false" attribute; any other value (including 'invalid' ones such as autocomplete="nope") will leave it enabled.
Conclusion
Autocompletion dropdowns cannot be turned off through the autocomplete dropdown; any value that disables Autofill will leave Autocomplete enabled, and vice versa. Anyone who thinks they've found a working solution (either through autocomplete or some other method such as CSS hacks) should check that it works against both mechanisms.
Unfortunately it's probably going to be an uphill struggle to convince Chrome's developers that this is broken. The developers of Autofill apparently believe that they made a calculated decision to break autocomplete="off" while offering web developers an alternative; that alternative is broken, for more subtle reasons than they realise. From their perspective, the resulting howls of protest are coming from disgruntled developers too lazy to jump through one little hoop and update their autocomplete="off" attributes. In all the noise, the message isn't getting through: the hoop is broken.

Try using type="search" instead of "text" for your input field, I've done this several time and it works for me.

As of Dec 6, 2019, with Chrome v78.x
Standard methods like autocomplete="off" are now working almost fine for the latest versions of Chrome. Except for this one:
This thing is a real bummer because it doesn't only disrespect the standard/non-standard values like "nope" but there's literally no way to turn this off unless the input is not even remotely related with "addressy" terms.
How on earth we could possibly display address-related input fields without using address-related words? Here comes the easiest solution ever.
Make sure the input element's name and id don't include any address-related terms. Attributes like id="input-street" or name="destination-zip" are big no-no.
This is the most crucial part: If you are required to use any human-readable address terms for the text input or any of its adjacent elements, insert the "invisible" zero width joiner (‌) between the letters of the said term. In this way, we can fool the AI capability of Chrome and bypass its strict autocompletion behavior.
Some working examples:
<input id="input-stret" placeholder="S‌treet" autocomplete="off">
<form action="/action_page.php">
<label for="product-addres">Product A‌ddress</label>
<input name="addres" id="product-addres" autocomplete="off">
</form>
And there you go. No more pesky menus for managing addresses, nor any regular autocompletion menus.

As gasman's answer explains, both the autofill and autocomplete features must be disabled, which doesn't seem possible on a single input.
The only working solution I've found is to setting autocomplete="off" on the input and add hidden fake inputs before the real input that fool autofill, like so:
<input name="Fake_Username" id="Fake_Username" type="text" style="display:none">
<input name="Fake_Password" id="Fake_Password" type="password" style="display:none">
<input name="NameInput" id="NameInput" type="text" autocomplete="off">

* This answer is incorrect. I've published a better (but uglier) solution as a new answer and kept this answer since some parts may still be useful. If that's not how to deal with incorrect answers on stackoverflow, feel free to delete this one *
Consider using autocomplete=<nonce>, where <nonce> is unique per field and across page loads.
For example, if a field is the N-th field created after the page was requested at timestamp TS, its <nonce> can be chosen to be nope_<TS>_<N>.
Effect on autocomplete: since <nonce> is a custom value for autocomplete, chromium does not activate the autocomplete function (see form_structure.cc).
Effect on autofill: chromium recognizes a field by comparing its fingerprint with those of earlier encountered fields (see form_field_data.cc). If recognized it may offer a list of remembered values. The fingerprints contain the value of the autocomplete attribute. Since no two nonces are equal, no field is recognized.
Notes:
The terms autocomplete and autofill as used here are swapped compared to gasman's reply.
All fields should be created dynamically on the client-side (unless you are willing to not have the page cached).

Disabling autofill:
Set autocomplete attribute to a non-standard value, e.g. "nope".
Disabling autocomplete:
The autocomplete function stores field names and their values when a form is submitted.
There's (almost, see note 1) nothing sensible to be done to prevent storage except setting autocomplete to "off"/"false" (see why).
Unfortunately that's not an option as it would enable autofill.
However it's possible to prevent retrieval of previous values by appending "!<nonce>" to the field names, where <nonce> is unique per page load
(thus making field names unrecognizable).
On the client side this can be achieved by something like the following line of javascript (upon page load):
Array.prototype.slice.call(document.body.getElementsByTagName('INPUT'))
.forEach(function(elt) { elt.name += '!' + new Date().getTime(); });
On the server side the part (if any) starting at "!" should be dropped from variable names (upon receiving post variables).
PS: this answer is an erratum to my earlier solution which is cleaner but wasn't sufficiently tested and - as gasman rightly pointed out - doesn't work for ordinary forms. This new solution was tested on Chrome Canary 79, does work, has relatively small impact and degrades nicely. Still, I feel guilty about publishing this hack and will feel even more guilty if I ever encounter it in real forms. It is *very* dirty.
Note 1: the only way to prevent storage that does make sense is to not set the name attribute in the first place (or to unset it), which necessitates intercepting the submit event to post the data "manually" (using XMLHttpRequest). Since the question is about forms and this strategy bypasses the traditional form-mechanism I've not elaborated on that approach. It's a nicer solution though.
Addendum: I decided to follow up on note 1 since I really dislike having a non-localized solution. Here's a localized version in vanilla JS that limits all impact to a single spot on the client side. Append it as a script to the document body or put it in the onload handler of the document.
function disableInputSuggestions(form) { // note: code uses ECMA5 features
// tweak the inputs of form
var inputs = Array.prototype.slice.call(form.getElementsByTagName('INPUT'));
var nonce = Date.now();
inputs.forEach(function(input, i) {
input.autocomplete = 'nope'; // prevent autocomplete
input.originalName = input.name || input.id; // to not let this code break form handling of inputs without names (browsers fallback to the id in that case)
input.name = nonce + '_' + i; // prevent autofill (if you're willing to eliminate all input ids first, then clear the name instead)
});
// replace the default submit handler by a custom one
form.onsubmit = function(ev) {
// get the form data using the original variable names
var formData = new FormData();
inputs.forEach(function(input) { formData.set(input.originalName, input.value); });
// submit the form data using XMLHttpRequest (alternatively, use a helper form or temporarily undo the tweaks to form)
var submitter = new XMLHttpRequest();
submitter.open(form.getAttribute('method'), form.getAttribute('action'));
submitter.onreadystatechange = function() {
if(submitter.readyState == 4 && submitter.status == 200) {
// handle the server response, here assuming the default form.target = "_self"
document.open();
document.write(submitter.responseText);
document.close();
}
}
submitter.send(formData);
return false; // prevent submitting form
};
}
disableInputSuggestions(document.forms.myForm); // assumed: the form has id = myForm

In Chrome 91
You need to use a random value, meaning a value that will change each time you load the page.
From the tests that I did, chrome seems to remember any attribute value that it already encountered and will suggest the last seen value for that attribute value the next time. So, if you put autocomplete="nope", chrome will remember that autocomplete="nope" is equal to the last value that you put in autocomplete="nope".
By using a unique random value that chrome has never seen, it won't suggest anything because it has never seen that value.
PHP 7 Example
<input type="text" name="firstname" autocomplete="<?= bin2hex(random_bytes(10)) ?>" />
Limitations
It seems to work on address fields but it has no effect on login fields. I haven't tested with credit card fields.

Chrome version 81.
For me, when input type is TEL, EMAIL or SEARCH, it WORKS with autocomplete='disabled'.
When input type is NUMBER, it WORKS with autocomplete='off'.
But when input type is TEXT .. it may works with autocomplete='off'. If not, it will do with autocomplete='disabled'.
You can try this, perhaps it will work for you (it works in 95% of cases for me) :
// Désactivation de l'autocomplete des input text
function setAutocomplete(val) {
var E = document.getElementsByTagName('INPUT');
for (var i = 0 ; i < E.length ; i++) {
if (E[i].name == 'txt_nom') { console.log('txt_nom', E[i]); }
var type = E[i].type.toUpperCase();
if (E[i].autocomplete != '') { continue; }
if (type == 'HIDDEN') {
//
} else if (type == 'NUMBER') {
E[i].autocomplete = 'off';
} else if ((type == 'TEL') || (type == 'EMAIL') || (type == 'SEARCH')) {
E[i].autocomplete = 'disabled';
} else {
E[i].autocomplete = val;
}
}
}
// Exécution de diverses fonctions à la fin de chaque chargement
window.addEventListener("load", function() {
// Désactivation de l'autocomplete des input text
setAutocomplete('off');
});

try this, I used this little trick and it worked until now (September 2020). i hope this works for a lot of people
--HTML--
<input type="text" name="example" autocomplete="off">
--Javascript--
let elements = document.querySelectorAll('[autocomplete="off"]');
elements.forEach(element => {
element.setAttribute("readonly", "readonly");
element.style.backgroundColor = "inherit";
setTimeout(() => {
element.removeAttribute("readonly");
}, 500);
})

I made a small jQuery plugin that disables any type of autocomplete feature from any browser
It is made to be used on the form tag, it takes few parameters and can be nested with other jQuery methods.
$('#login_form').randomizeFormFields();
It transforms this:
<form id="login_form" action="" method="post">
<input type="text" name="email">
<input type="password" name="secret">
</form>
Into this:
<form id="login_form" action="" method="post">
<input type="text" name="yQoiFZkCrzwWXN3WWgM8Jblby">
<input type="password" name="ono1qamA9CzrH4tW2COoRtFKI">
</form>
It preserves the original names upon submit
// returned post (php example)
array(2) {
["email"]=>
string(16) "email#domain.com"
["secret"]=>
string(19) "supersecretpassword"
}
https://github.com/cicerogeorge/randomize-form-fields
Please fork it if you have ideas

I have tried with autocomplete = "off" and autocomplete = "nope" for EmailId textbox in html form but it is not working for Google Chrome. So I tried with below changes that worked for me.
<input type="email" class="tbx-input" name="Email" style="display:none;">
<input type="email" class="tbx-input" name="Email" id="Email" placeholder=" " required autocomplete="nope">

I couldnt get any of these suggestions to work... so I just made my input a textarea, made cols="1" and disabled adjusting the space.
<textarea style="resize: none;" type='search' class="form-control" rows="1" cols="50" .... >
No more suggestions

autocomplete='nope'
This is the current working solution for Chrome 76.

Related

Firefox input type=date min not opening on minimum valid month

I'm not sure if there's a way around this, but Firefox doesn't play nicely when you're using input type="date" with a min= attribute: It always open the datepicker on the current month, rather than the month where the minimum valid date begins. This is particularly annoying if the date is in the future.
For example:
<input type="date" min="2021-08-04">
(See JSFiddle in Firefox.)
The user has to manually scroll through months until they finally get to the one that's available. Less than ideal!
One way to bypass this behaviour is to set a value to the input as suggested in the comments. Instead of setting value attribute in the HTML, you can try to set it programmatically when the user clicks on the input and the datepicker is shown.
I think that focus/focusin are the best events to use to catch, since as far as I know there is no show/open events on input[type="date"].
On MDN page, in the Events sections are mentioned only change and input.
Here a live sample:
var dateControl = document.querySelector('input[type="date"]');
dateControl.addEventListener("focus", function(){
// You can add validation on value
if( this.min && !this.value ){
this.value = this.min;
}
});
<input type="date" min="2021-08-08">
I know this is not a really great solution as the browser can be tricked any time.
But this should work too.
HTML : <input id="dateInput" type="date" min="2021-08-08">
You can identify the browser of the client if it is Firefox and change the date automatically to become the minimum through this JS:
if (navigator.userAgent.indexOf("Firefox") != -1) {
document.getElementById("dateInput").value = document.getElementById("dateInput").min;
}
You can use value attribute to set the default value.
<input type="date" min="2021-08-04" value="2021-08-04">

SuiteCRM turn ON autocomplete on email recipient field

I'm trying to remove the autocomplete="off" attribute from the input field for the email recipient field
The code of the input field is as follows:
<input
class="ac_input yui-ac-input"
size="96"
id="addressTO1"
title="An"
name="addressTO1"
onkeyup="SE.composeLayout.showAddressDetails(this);"
autocomplete="off" type="text">
Is there a setting that can be accessed via the GUI that changes the behaviour of the email "to" field or what would be the best way to turn on the autocomplete functionality.
Just removing it does not work, as there is some javascript functionality there, that keeps bringing it back.
I'm using suiterCRM version 7.6.5
Sugar Version 6.5.23.
There is no GUI for changing that, the autocomplete=off is hardcoded.
The TO field (addressTO1 dynamically generated name) is not a regular text input field, it will support multiple emails separated by comma, so a autocomplete="email" will not work.
The only solution I could come up with is for you to create a javascript function and attach to the keyup event of the field and show your logic.
Also you will need to save previously filled input with the answers and provide a mechanism to delete that. Not an easy solution I am afraid.

Lock/unlock form with toggle

I'm looking for a way to have a button toggeling the field of my form. When "locking" the form with the toggle button no data can be typed. When "unlocking" data should be allowed to be typed. What I want to achieve with this is simple avoiding users to accidentally type.
I found the code below and it works. Only problem is that it only applies to one input field. I want it to work on more that one.
<input type="checkbox" id="yourBox">
<input type="text" id="yourText">
<script>
document.getElementById('yourBox').onchange = function() {
document.getElementById('yourText').disabled = this.checked;
};
</script>
Mark the fields you want to disable with a CSS class, and then use jQuery to disable them.
jQuery - Disable Form Fields
If you want a pure Javascript solution, just repeat this line
document.getElementById('yourText').disabled = this.checked;
for each field.
Or, you can do something like this this: How to Get Element By Class in JavaScript?. Note that you can assign multiple CSS classes to the same field, so assign another class to identify those fields that need to be disabled.

CFForm vs Form in Coldfusion

I have been using plain forms and input fields in coldfusion for some time now but recently discovered that cfinput tags will automagically prevent some xss attacks for me. This has made me wonder, if there is any disadvantages to using cffrom and cfinput over normal form and input tags in coldfusion.
The only disadvantage I have found in the short time looking into it is that it adds 2 external style sheets and 1 script tag to the page.
so in short:
What are the advantages and disadvantages of using CFFORM over FORM in coldfusion?
I prefer to write my own JS around my forms. I started out with cfform back in the day, but eventually wanted to do more robust things (validations, etc) than cfform was able to handle. That forced me to learn JS, and I've been very happy writing my own JS since.
So I guess I'd say one big drawback is that you're restricted to what cfform can handle. Depending on your situation, that might be fine.
Another drawback that I ran into a long time ago (which to be fair, may have been addressed since), is that the JS generated by cfform would conflict or interfere with my hand-written JS.
It'll certainly come down to preference. It's neither "right" nor "wrong" to use cfform or regular forms. For me, I prefer to be able to do whatever manipulation I need to do manually, as there are no restrictions/limitations.
I have a love-hate relationship with <cfform> & <cfinput>.
To have the same xss protection that CFFORM provides, just wrap htmlEditFormat() around value="" in regular like so:
<input name="x" value="#htmlEditFormat(x)#">
For even better XSS protection, use OWASP Enterprise Security API (.jar included in one of the CF9 latest hotfixes)
I love how I can do ajaxified form easily without writing JS, but I hate how it generates lots of ugly JavaScript and loads up lots of JS and css files for something rather simple. So I've decided to use cfform for internal sites only and not for public facing site (performance issue).
Other then the ajax features, the checked attribute that accepts CF boolean and populating select with query object are features that cfinput and cfselect provide which can be quite useful.
Use the right tool for the right job. If you found the feature of <cfform> useful, use it. Just know its limitations, and decide for yourself.
I have been using ColdFusion for almost 14 years. The reason that CF is such a kick-ass product is that empowers new users to get a lot of work done quickly with not much understanding and it enables rocket scientists to build really powerful and secure applications quickly too.
CFFFORM, CFINPUT, CFLAYOUT, CFPOD are tags that are created for new users. Basically, they are training wheels. If you're new to web development, you should give this tags a try. As you gain experience, you'll want to drop these tags and move onto other techniques to create more robust applications.
There's nothing wrong with these tags, just like there's nothing wrong with training wheels. You just need to know that there's an appropriate tool for each job. Actually, there are lots of appropriate tools for each job.
Currently, I am developing a ColdFusion 9 / jQuery / SQL Server intranet that builds external web sites. I am doing it without using a single form tag. And, I am doing it completely in CFSCRIPT. Whoa!
Using jQuery, you don't need forms. You just need inputs. Here's how I create an input in CFSCRIPT.
<cfscript>
Options = "";
for (i = 1; i lte 10; i++) {
Options = Options & wrapOption("Some choice #i# ", i);
}
SelectBox = wrapSelect(Options, "MySelectID");
writeOutput(SelectBox);
SecretDiv = wrapDiv("", "", "MyDivID");
writeOutput(SecretDiv);
</cfscript>
The user defined functions to create the HTML are in my UDF_Library.cfm file:
// WRAP SELECT
function wrapSelect(SelectContent, Class, ID) {
LOCAL.SelectContent = ARGUMENTS.SelectContent;
LOCAL.Properties = "";
// CLASS
if (isDefined("ARGUMENTS.Class")) {
LOCAL.Properties = LOCAL.Properties & " class='#ARGUMENTS.Class#'";
}
// ID
if (isDefined("ARGUMENTS.ID")) {
LOCAL.Properties = LOCAL.Properties & " id='#ARGUMENTS.ID#'";
}
LOCAL.Item = "<select #LOCAL.Properties#>#LOCAL.SelectContent#</select>";
return LOCAL.Item;
}
// WRAP OPTION
function wrapOption(Content, Value, Selected) {
LOCAL.Content = ARGUMENTS.Content;
LOCAL.Properties = " value='#ARGUMENTS.Value#'";
// SELECTED
if (isDefined("ARGUMENTS.Selected") and (ARGUMENTS.Selected eq "selected")) {
LOCAL.Properties = LOCAL.Properties & " selected";
}
LOCAL.Item = "<option #LOCAL.Properties#>#LOCAL.Content#</option>";
return LOCAL.Item;
}
// CREATE DIV
function wrapDiv(Content, Class, ID) {
LOCAL.Properties = "";
// CLASS
if (isDefined("ARGUMENTS.Class")) {
LOCAL.Properties = LOCAL.Properties & " class='#ARGUMENTS.Class#'";
}
// ID
if (isDefined("ARGUMENTS.ID")) {
LOCAL.Properties = LOCAL.Properties & " id='#ARGUMENTS.ID#'";
}
LOCAL.Item = "<div #LOCAL.Properties#>#ARGUMENTS.Content#</div>";
return LOCAL.Item;
}
I use jQuery and refer to every element by its class or ID. If you do that, you can submit the data in each element to an ajax call like this:
<script type="text/javascript">
$(document).ready(function() {
$("#MySelectID").change(function() {
MyID = $("#MySelectID").val();
$("#MySecretDiv").load("CoolQuery.cfm?UserID"+MyID);
});
});
</script>
The point is, that as long as you are using CFFORM and CFINPUT, you can't do all the really powerful jQuery stuff. But, you need those tags to get started.
2012 is going to be a kick-ass year for the power of ColdFusion and jQuery!!!
Good luck!
I haven't used ColdFusion's CFInput in a long while. I've been using the jQuery Validation plugin so that I can perform validation on other things like:
is the element visible? (ie, hide a section if not essential, but eliminate the requirement if not shown.)
is a checkbox checked? (ie, you checked "other", now fill-in-the-blank is required.)
is it a valid date/time value? (ie, I additionally use the DateJS library to assist in this)
perform ajax query to determine if username is unique
is the URL entered valid?
compare password1 with password2
custom rules based on a combination of things
Most validation rules can be added inline to the class parameter:
<input type="text" name="Name" class="required">
<input type="text" name="Birthdate" class="required date">
<input type="text" name="Email" class="required email">
<input type="text" name="Website" class="url">
I prefer to use jQuery because sometimes I need to add this same logic to a non-ColdFusion based form and I don't have to worry about the fact the CFInput is a ColdFusion-Only tag.
Here's a link with more information regarding the jQuery Validation library:
http://bassistance.de/jquery-plugins/jquery-plugin-validation/

Programmatically submitting a form while using AjaxForm

I wanted to find a way to upload a single file*, in the background, have it start automatically after file selection, and not require a flash uploader, so I am trying to use two great mechanisms (jQuery.Form and JQuery MultiFile) together. I haven't succeeded, but I'm pretty sure it's because I'm missing something fundamental.
Just using MultiFile, I define the form as follows...
<form id="photoForm" action="image.php" method="post" enctype="multipart/form-data">
The file input button is defined as...
<input id="photoButton" "name="sourceFile" class="photoButton max-1 accept-jpg" type="file">
And the Javascript is...
$('#photoButton').MultiFile({
afterFileSelect: function(){
document.getElementById("photoForm").submit();
}
});
This works perfectly. As soon as the user selects a single file, MultiFile submits the form to the server.
If instead of using MultiFile, as shown above, let's say I include a Submit button along with the JQuery Form plugin defined as follows...
var options = {
success: respondToUpload
};
$('#photoForm').ajaxForm(options);
... this also works perfectly. When the Submit button is clicked, the form is uploaded in the background.
What I don't know how to do is get these two to work together. If I use Javascript to submit the form (as shown in the MultiFile example above), the form is submitted but the JQuery.Form function is not called, so the form does not get submitted in the background.
I thought that maybe I needed to change the form registration as follows...
$('#photoForm').submit(function() {
$('#photoForm').ajaxForm(options);
});
...but that didn't solve the problem. The same is true when I tried .ajaxSubmit instead of .ajaxForm.
What am I missing?
BTW: I know it might sound strange to use MultiFile for single-file uploads, but the idea is that the number of files will be dynamic based on the user's account. So, I'm starting with one but the number changes depending on conditions.
The answer turns out to be embarrassingly simple.
Instead of programmatically submitting using...
document.getElementById("photoForm").submit();
... I used...
$("#photoForm").submit();
Also, since I only need to upload multiple files on occasion, I used a simpler technique...
1) The form is the same as my original...
<form id="photoForm" action="image.php" method="post" enctype="multipart/form-data">
2) The file input field is basically the same...
<input id="photoFile" "name="sourceFile" style="cursor:pointer;" type="file">
3) If the file input field changes, submit is executed...
$("#photoFile").change(function() {
$("#photoForm").submit();
});
4) The AjaxForm listener does its thing...
var options = {
success: respondToUpload
};
$('#photoForm').ajaxForm(options);