Better modern input type detection

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.

The type check

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.

The Hello World check

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.

The script

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.

Discussion

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:

  1. The browser doesn’t see Hello World as an error. In that case we are certain that it does not support the requested type.
  2. The browser doesn’t support 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.
    And yes, this use of 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.

tel

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.

What doesn’t work

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

This 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, mobile platform strategist, consultant, and trainer. You can also follow him on Twitter.
Atom RSS

I’m around at the following conferences:

(Data from Lanyrd)

Categories:

Monthlies: