During my research of modern input types such as email
, date
, and number
I stumbled upon the Hello World
JavaScript detection technique. After fairly extensive testing I concluded that it should be added to the customary type === 'text'
detection, mostly in order to cater to Android WebKit, although it also solves a few other problems.
Although my new script isn’t perfect, it gives better results than just the type test, and people interested in such matters should look into this.
Traditionally, modern input types are detected by creating an input with a type of email
or number
or whatever you want to test, and then querying type
. If the modern type you just set is reported, the browser supports that type. Non-supporting browsers return text
instead.
var test = document.createElement('input'); test.type = 'number'; // or any other type if (test.type === 'text') { // not supported; proceed to fallback } else { // type is supported }
The big problem with this detection is that it does not work in Android WebKit. This browser always returns the type you set, regardless of whether it supports it or not. There are also a few miscellaneous problems.
Someone on Twitter (lost tweet) told me he always uses the Hello World
detection, and after some fairly extensive testing I found that we have to add this detection to our standard scripts.
The test is very simple: set the input’s value to Hello World
, which is not a number, time, date, email, url, or phone number. If the browser accepts the value it’s likely (though not certain) that the browser doesn’t support the type.
var test = document.createElement('input'); test.type = 'number'; // or any other type test.value = 'Hello World'; var helloWorldAccepted = (test.value === 'Hello World');
This test alone is not enough; we need to combine it with the traditional type-based test and two special tests.
This is the new test script. You send it an HTML input element and it returns its best guess as to whether the type is supported.
function supportsType(input) { var desiredType = input.getAttribute('type'); var supported = false; if (input.type === desiredType) { supported = true; } input.value = 'Hello world'; var helloWorldAccepted = (input.value === 'Hello world'); switch (desiredType) { case "email": case "url": if (!input.validationMessage) { supported = false; } break; case "color": case "date": case "datetime": case "month": case "number": case "time": case "week": if (helloWorldAccepted) { supported = false; } break; } input.value = ''; return supported; }
Let’s go through an old-fashioned line by line discussion as I used to do ten years ago.
function supportsType(input) { var desiredType = input.getAttribute('type');
First, a bit of good news. input.getAttribute('type')
always, in all 50 or so browsers I tested, returns the type actually defined in the HTML, even if the browser doesn’t support it. This allows us to always find the desired type.
If you think this is logical, you’re right. If you think that solely because something is logical all browsers will surely support it, you’re not paranoid enough to be a browser researcher, even though you happen to be right in this particular instance.
var supported = false; if (input.type === desiredType) { supported = true; }
Then we do the traditional type test. It’s still a very important, though not enough by itself. Remember that Android WebKit always succeeds on this test, even if it does not support the type.
input.value = 'Hello world'; var helloWorldAccepted = (input.value === 'Hello world');
Now we do the Hello World
test. We set the value of the input and figure out if the browser accepts it. We do not draw conclusions yet; this was just preparation for the final steps.
switch (desiredType) { case "email": case "url": if (!input.validationMessage) { supported = false; } break;
Now we study the email
and url
types. They have meanwhile received the value Hello World
, which is neither an email address nor a URL. We now look at validationMessage
, which should contain the error message that’s presented to the user.
There are two reasons why there wouldn’t be a validation message:
Hello World
as an error. In that case we are certain that it does not support the requested type.validationMessage
at all. It turns out that the only browser that does not is Android WebKit, which also doesn’t support email
and url
. Thus the script gives the correct answer.validationMessage
is theoretically questionable because I solve one bug by detecting another. Since Android WebKit is not under development any more and its behaviour will never change, this detection is safe in practice.Unfortunately Safari iOS has, once again, a bug. It generates a validationMessage
when you enter Hello World
in an email
or url
, but it doesn’t actually do anything. It sends the faulty value to the server and never shows the error message it generated. Therefore this test misfires in Safari. Then again, the old type check also misfires. Safari is just a buggy browser.
case "color": case "date": case "datetime": case "month": case "number": case "time": case "week": if (helloWorldAccepted) { supported = false; } break;
In most other cases we see if Hello World
has been accepted. If it has the browser likely doesn’t support the type.
However, there’s a fairly important catch here. If Hello World is rejected we have not proven that the browser fully supports the type. Instead, we have proven that the browser won’t send wrong values to the server. There’s no guarantee that it will inform the user of that fact, though.
As an example, let’s again look at Safari iOS and the number
type. This type gives you the numeric part of the regular keyboard, but doesn’t prevent you from switching to the alphabetical part and entering, say, Hello World.
Secretly Safari checks the value, and it does not send non-numeric values to the server. However, it fails to notify the user of that, so from the user’s perspective it’s perfectly possible to submit a number
with value Hello World
. The value never ends up on the server, but the user has no way of figuring that out.
This sending-to-server step is what is detected here. According to this test Safari iOS supports number
because it does some sort of validation. The same goes for a few other browsers. That may not be entirely what you’re looking for, but it’s the best you’re going to get. The type === 'text'
test doesn’t give any better results.
input.value = ''; return supported; }
Finally, set the value to the empty string and report on the browser’s support for the desired type.
You may have noticed that the tel
type is missing. That’s because there’s something interesting going on here. All mobile browsers show the phone keyboard for tel
; that is, the numeric keyboard with a few extra characters. That’s logical and desirable.
Note: all browser, including Safari iOS. iOS actually contains a purely numeric keyboard. Then why doesn’t Safari use it for number
? I have no clue.
Anyway. Because the interface makes it impossible to fill in anything that doesn’t belong in a phone number, browsers do not bother to validate the value. That means that the Hello World
value is accepted without comment, and that the Hello World test will not work for tel
. So for this type I stick to the traditional type === 'text'
test.
This detection, though better than a pure type === 'text'
test, isn’t perfect. Here are some of the misdetects I found; see also my full test report (unedited text).
number
: not quite a false positive, but you have to be aware of its odd treatment of this typeemail
and url
: false positivenumber
: same problem as Safarimonth
and week
: false positivedatetime
, month
, and week
. Yes, Firefox pretends it does not support them while in fact supporting them.datetime
: again a false negativeemail
and url
on Chromium 30 WebView: false positiveemail
and url
: wrong data is not sent to the server, but the user is not notified of that factThis is not good. I spent several days trying to refine the script in order to get around some of these problems, but in the end had to concede defeat. Despite this my script is still better than the traditional type === 'text'
test because that test gives a whole slew of false positives in Android WebKit, while mine doesn’t generate quite as many.
This is the blog of Peter-Paul Koch, web developer, consultant, and trainer.
You can also follow
him on Twitter or Mastodon.
Atom
RSS
If you like this blog, why not donate a little bit of money to help me pay my bills?
Categories: