Outputs of a Ruby-on-Rails button_to
have started failing in W3C validation recently, with the returned message of:
An input element with a type attribute whose value is hidden must not have an autocomplete attribute whose value is on or off.
So I would like to get rid of the error.
The corresponding button_to
statement in View (ERB) in Rails-7.0.4 is
<%= button_to "Add Entry", new_article_path, form_class: "inline_form button_to",
method: :get, params: { a_token: "abc" } %>
which produces an HTML (simplified!):
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="utf-8"> <title>My Title</title> </head>
<body>
<form class="inline_form button_to" method="get" action="/articles/new?locale=en">
<input type="submit" value="Add Entry" />
<input type="hidden" name="a_token" value="abc" autocomplete="off" />
</form>
</body>
</html>
If I paste this in the W3C validator form, it fails with the above-mentioned message.
Apparently, the W3C validator does not like the presence of the attribute autocomplete
in the input
tag with type="hidden"
. But that is the default output of button_to
(in Rails 7.0).
It seems the Rails team added autocomplete=off
last year to make the HTML output Firefox-proof according to Issue #42610 in Rails repository. In the associated discussion, user jpwynn says (2021-06-27) the change would not break the W3C validation. But seemingly it does now……
As far as I am aware, I have modified no relevant part in my Rails app. Yet, I notice routine testing has started to fail at W3C validations. So, I suspect W3C has changed the specification very recently (but I may be wrong!).
My Rails test is pretty simple as follows, using Gem w3c_validators Ver.1.3.7; it just detects any errors and fails if there is any:
# /test/test_helper.rb
require 'w3c_validators'
def my_w3c_validate
arerr = @validator.validate_text(response.body).errors
assert_equal 0, arerr.size,
"Failed in W3C-validation: ("+arerr.map(&:to_s).join(") (")+")"
end
with Gemfile
:
group :development, :test do
gem 'w3c_validators', '~> 1', '>= 1.3.6' # => 1.3.7 in reality
end
So, is the output of Rails button_to
a valid HTML? If not, how can I modify it so that the output HTML will be a valid HTML?
I am using the most recent stable version, Rails-7.0.4.
The maintainer of W3C HTML checker (validator) @sideshowbarker kindly gave information (see comments in the question).
Here is a summary:
<input type="hidden" name="abc" autocomplete="off">
is invalid.
According to the HTML Form specification,
When wearing the autofill anchor mantle, the autocomplete attribute, if specified, must have a value that is an ordered set of space-separated tokens consisting of just autofill detail tokens (i.e. the "on" and "off" keywords are not allowed)” — where “wearing the autofill anchor mantle
which is basically applied to the <input type=hidden>
tag.
button_to
method violates the spec of the <input>
tag.
button_to
changed recently (last year) to deal with Firefox's mal-behaviour; it did pass the W3C HTML validation at that time! See Rails Github Issue #42610Now, since the W3C validator is doing right, you have two options:
button_to
Here is the measure in the second policy. You pass the Array of W3CValidators::Message
and this method returns the same Array but with the said-errors removed. The original relevant error message can be recorded in Logger
. I put the script (but with more extensive description) in Github Gist, too.
# @example Usage, maybe in /test/test_helper.rb
# # Make sure to write in /config/environments/test.rb
# # config.ignore_w3c_validate_hidden_autocomplete = true
# #
# #require 'w3c_validators'
# errors = @validator.validate_text(response.body).errors
# errors = _may_ignore_autocomplete_errors_for_hidden(errors, "W3C validaiton failed: ")
# assert_empty errors, "Failed in W3C validation: "+errors.map(&:to_s).inspect
#
# @param errs [Array<W3CValidators::Message>] Output of +@validator.validate_text(response.body).errors+
# @param prefix [String] Prefix of the warning message recorded with Logger.
# If empty, no message is recorded in Logger.
# @return [Array<String>]
def may_ignore_autocomplete_errors_for_hidden(errs, prefix="")
removeds = []
return errs if !Rails.configuration.ignore_w3c_validate_hidden_autocomplete
errs.map{ |es|
# Example of an Error:
# ERROR; line 165: An “input” element with a “type” attribute whose value is “hidden” must not have an “autocomplete” attribute whose value is “on” or “off”
if /\AERROR\b.+\binput\b[^a-z]+\belement.+\btype\b.+\bhidden\b.+\bautocomplete\b[^a-z]+\battribute\b/i =~ es.to_s
removeds << es
nil
else
es
end
}.compact
ensure
# Records it in Logger
if !removeds.empty? && !prefix.blank?
Rails.logger.warn(prefix + removeds.map(&:to_s).uniq.inspect)
end
end