HTML5 Feature Detection: Presence vs Behaviour
Most HTML5 feature detection articles get it wrong.
The classic approach was:
<syntaxhighlight lang="javascript"> if(document.createElement("canvas").getContext) {
// Canvas supported
} </syntaxhighlight>
Or even worse:
<syntaxhighlight lang="javascript"> if(window.localStorage) {
// LocalStorage supported
} </syntaxhighlight>
The problem is that these tests only prove that an API exists. They do not prove that it is functional.
A browser may:
- Expose the object but not implement it correctly.
- Expose a partial implementation.
- Disable the feature via policy.
- Restrict the feature in private browsing mode.
- Provide a vendor stub.
- Fail at runtime despite passing existence checks.
This was a common problem during the early HTML5 transition years.
The Wrong Question
Many developers ask:
Does the browser support this feature?
What they should ask is:
Can this browser successfully perform the operation I need?
These are very different questions.
Example 1: Creating Elements
A common test was:
<syntaxhighlight lang="javascript"> var article = document.createElement("article");
if(article) {
// HTML5 semantic element supported
} </syntaxhighlight>
Unfortunately, this proves almost nothing.
Every browser capable of executing JavaScript can create an unknown element.
Even Internet Explorer 6 can do this:
<syntaxhighlight lang="javascript"> var unicorn = document.createElement("unicorn"); </syntaxhighlight>
The object exists.
That does not mean:
- The element participates correctly in layout.
- CSS selectors work.
- Accessibility APIs understand it.
- The browser recognises its semantic meaning.
A better test would be:
<syntaxhighlight lang="javascript"> var article = document.createElement("article");
document.body.appendChild(article);
var display =
window.getComputedStyle(article).display;
document.body.removeChild(article);
if(display === "block") {
// Behaviour resembles a modern browser
} </syntaxhighlight>
Now we are testing behaviour rather than object creation.
Example 2: Canvas
The traditional test:
<syntaxhighlight lang="javascript"> if(document.createElement("canvas").getContext) {
// Canvas supported
} </syntaxhighlight>
This only proves a method exists.
A stronger test:
<syntaxhighlight lang="javascript"> var canvas = document.createElement("canvas"); var context = canvas.getContext("2d");
if(context) {
context.fillStyle = "red"; context.fillRect(0,0,10,10);
var pixel =
context.getImageData(5,5,1,1).data;
if(pixel[0] === 255)
{
// Drawing actually works
}
} </syntaxhighlight>
Now we're validating actual rendering functionality.
Example 3: LocalStorage
This one catches countless developers.
They write:
<syntaxhighlight lang="javascript"> if(window.localStorage) {
// Supported
} </syntaxhighlight>
Yet some browsers expose the API while preventing usage.
The correct test is:
<syntaxhighlight lang="javascript"> try {
localStorage.setItem("_test","1");
var value =
localStorage.getItem("_test");
localStorage.removeItem("_test");
if(value === "1")
{
// Functional LocalStorage
}
} catch(ex) {
// Not usable
} </syntaxhighlight>
Here we're testing:
- Write
- Read
- Delete
The actual behaviour required by the application.
Example 4: Drag and Drop
Many detection scripts do:
<syntaxhighlight lang="javascript"> if("draggable" in document.createElement("div")) {
// Drag and drop support
} </syntaxhighlight>
Yet actual drag operations may fail.
A more useful validation might verify:
<syntaxhighlight lang="javascript"> var element =
document.createElement("div");
var supported =
typeof element.ondragstart !== "undefined";
</syntaxhighlight>
And even that is only a preliminary test.
The true verification occurs when actual drag events fire during operation.
Example 5: Input Types
A famous HTML5 detection trap.
Developers test:
<syntaxhighlight lang="javascript"> var input =
document.createElement("input");
input.type = "date";
if(input.type === "date") {
// Date picker supported
} </syntaxhighlight>
This is already better than existence testing because we're observing browser behaviour.
Browsers that don't understand the type usually fall back to:
<syntaxhighlight lang="javascript"> "text" </syntaxhighlight>
But even then:
- Does a calendar appear?
- Is validation performed?
- Is localisation correct?
Again, behaviour matters.
The Modern Principle
HTML5 support should be viewed as a spectrum:
| Level | Test |
|---|---|
| API Exists | Object present |
| API Usable | Methods callable |
| Feature Works | Expected behaviour |
| Feature Reliable | Behaviour matches specification |
| Feature Suitable | Meets application requirements |
Most detection libraries stop at Level 1 or 2.
Applications actually need Level 3 or 4.
A Better Pattern
Rather than:
<syntaxhighlight lang="javascript"> if(window.indexedDB) { } </syntaxhighlight>
Use capability tests:
<syntaxhighlight lang="javascript"> async function testIndexedDB() {
try
{
const request =
indexedDB.open("test");
return new Promise(function(resolve)
{
request.onsuccess =
function()
{
resolve(true);
};
request.onerror =
function()
{
resolve(false);
};
});
}
catch(ex)
{
return false;
}
} </syntaxhighlight>
This verifies that the browser can actually perform the capability required.
The Real Rule
A good HTML5 feature detector should answer:
"Can I successfully perform the operation I need right now?"
not:
"Does some JavaScript object exist?"
The difference is the same as checking whether a hard drive appears in Device Manager versus actually reading and writing a file to it.
One proves presence.
The other proves functionality.
Conclusion
The web industry has historically focused on feature detection, but modern applications benefit far more from capability detection.
Rather than checking whether a browser exposes an API, applications should verify that the API can successfully perform the required task under real-world conditions.
The safest approach is therefore:
- Detect the feature.
- Test the capability.
- Validate the expected behaviour.
- Gracefully degrade where necessary.
By shifting from presence testing to behavioural testing, developers can build applications that are more robust, more portable, and more resilient to browser quirks, partial implementations, privacy restrictions, and future platform changes.
In short:
Don't test whether a browser claims to support HTML5.
Test whether it can actually do the job.