Snippets

Hiding DOM elements - ally.js

This document explains the various ways of hiding things and the implications that come with that.

When we say an element is hidden, we usually mean it is not visible. However, the screen is not the only output mechanism we may need to hide content from. Systems like screen readers and braille displays rely on a document’s representation in the accessibility tree. For disambiguation we’ll use the following terms:

Completely hidden
The element is not rendered on screen, not exposed in the accessibility tree, not accessible to keyboard navigation.
Semantically hidden
The element is rendered on screen, but not exposed in the accessibility tree, and still accessible to keyboard navigation.
Visually hidden
The element is not rendered on screen, but exposed in the accessibility tree, and still accessible to keyboard navigation.

[…]

How to hide elements completely

Completely hiding elements can be done in 3 ways:

  • via the CSS property display, e.g. display: none;
  • via the CSS property visibility, e.g. visibility: hidden;
  • via the HTML5 attribute hidden, e.g. <span hidden>

While each of these techniques has the same end result, i.e. content is not rendered and not exposed in the accessibility tree, they have different behaviors.

[…]

How to hide elements semantically

To hide content from the accessibility tree but retain the content on screen, we may use the attribute aria-hidden="true".

[…]

2016 edition of .visuallyhidden

[…]

Code language: CSS

.visuallyhidden:not(:focus):not(:active) {
  position: absolute;
 
  width: 1px;
  height: 1px;
  margin: -1px;
  border: 0;
  padding: 0;
 
  clip-path: inset(100%);
  clip: rect(0 0 0 0);
  overflow: hidden;
}
  • It works in all modern browsers including Internet Explorer 9 - 11.
  • It side-steps the need to re-style everything for focusable elements such as skip-links.
  • It accounts for the deprecated clip property.

Keyboard navigation

The techniques to hide elements only visually or semantically come with a caveat. Focusable elements like <a href="…"> remain keyboard navigatable, even though the element is not visible on screen or not exposed in the accessibility tree.

To make sure sighted keyboard users do not end up focusing elements they can’t see, and screen reader users not focusing element’s that don’t exist for them, we need to make sure that partially hidden content is not accessible to keyboard navigation using the Tab and Shift Tab keys. To accomplish this, we can add tabindex="-1" to the elements we want to hide from the keyboard.

Recap

  • Use the hidden attribute to completely hide an element.
  • Use the aria-hidden attribute to hide an element from the accessibility tree.
  • Use the .visuallyhidden class to hide an element from the screen.
  • Use visibility: inherit; instead of visibility: visible; to avoid accidentally showing content.
  • Do not attach any CSS styles to the aria-hidden attribute.
  • Take care of keyboard focusable content that is partially hidden by adding tabindex="-1".

Alex Feyerke: Step Off This Hurtling Machine | JSConf.au 2014

I thought this was an excellent talk on the hard questions we should be asking ourselves as developers. Why do most people use closed, proprietary systems and devices, if the open web is so wonderful? Even as developers, we still use them ourselves, and depend on them. How can we be more empathetic to what the average user needs and wants? How can we lock open the web, so the future isn’t entirely dependent on huge corporations and services, which is where we seem to be heading?

The problems with feature detection

The principle of feature detection is really simple. Before you use a particular API you test if it is actually available. If it is not, you can provide an alternative or fail gracefully. Why is this necessary? Well, unlike HTML and CSS, JavaScript can be very unforgiving. If you would use an API without actually testing for its existence and assume it just works you risk that your script will simply throw an error and die when it tries to call the API.

Take the following example:

Code language: JavaScript

if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(function(pos) {
    alert('You are at: ' + pos.coords.latitude + ', ' + pos.coords.longitude);
  });
}

Before we call the getCurrentPosition() function, we actually check if the Geolocation API is available. This is a pattern we see again and again with feature detection.

If you look carefully you will notice that we don’t actually test if the getCurrentPosition() function is available. We assume it is, because navigator.geolocation exists. But is there actually a guarantee? No.

[…]

Cutting the mustard

There is another principle that has gotten very popular lately. By using some very specific feature tests you can make a distinction between old legacy browsers and modern browsers.

Code language: JavaScript

if ('querySelector' in document
  && 'localStorage' in window
  && 'addEventListener' in window) 
{
  // bootstrap the javascript application
}

In itself it is a perfectly valid way make sure the browser has a certain level of standards support. But at the same time also dangerous, because supporting querySelector, localStorage and addEventListener doesn’t say anything about supporting other standards.

Even if the browser passes the test, you really still need to do proper feature detection for each and every API you are depending on.

[…]

There are features where the whole premise of feature detection just fails horribly. Some browsers ship features that are so broken that they do not work at all. Sometimes it is a bug, and sometimes it is just pure laziness or incompetence. That may sound harsh, but I’m sure you agree with me at the end of this article.

The most benign variants are simply bugs. Everybody ships bugs. And the good browsers quickly fix them. Take for example Opera 18 which did have the API for Web Notifications, but crashed when you tried to use it. Blink, the rendering engine, actually supported the API, but Opera did not have a proper back-end implementation. And unfortunately this feature got enabled by mistake. I reported it and it was fixed in Opera 19. These things happen.

Introducing Resilient Web Design

I wrote a thing. The thing is a book. But the book is not published on paper. This book is on the web. It’s a web book. Or “wook” if you prefer …please don’t prefer. Here it is:

Resilient Web Design.

It’s yours for free.

Much of the subject matter will be familiar if you’ve seen my conference talks from the past couple of years, particularly Enhance! and Resilience. But the book ended up taking some twists and turns that surprised me. It turned out to be a bit of a history book: the history of design, the history of the web.

Resilient Web Design is a short book. It’s between sixteen and seventeen megawords long. You could read the whole thing in a couple of hours. Or—because the book has seven chapters—you could take fifteen to twenty minutes out of a day to read one chapter and you’d have read the whole thing done in a week.

If you make websites in any capacity, I hope that this book will resonate with you. Even if you don’t make websites, I still hope there’s an interesting story in there for you.

You can read the whole book on the web, but if you’d rather have a single file to carry around, I’ve made some PDFs as well: one in portrait, one in landscape.

I’ve licensed the book quite liberally. It’s released under a Creative Commons attribution share-alike licence. That means you can re-use the material in any way you want (even commercial usage) as long as you provide some attribution and use the same licence. So if you’d like to release the book in some other format like ePub or anything, go for it.

I’m currently making an audio version of Resilient Web Design. I’ll be releasing it one chapter at a time as a podcast. Here’s the RSS feed if you want to subscribe to it. Or you can subscribe directly from iTunes.

I took my sweet time writing this book. I wrote the first chapter in March 2015. I wrote the last chapter in May 2016. Then I sat on it for a while, figuring out what to do with it. Eventually I decided to just put the whole thing up on the web—it seems fitting.

Whereas the writing took over a year of solid procrastination, making the website went surprisingly quickly. After one weekend of marking up and styling, I had most of it ready to go. Then I spent a while tweaking. The source files are on Github.

I’m pretty happy with the end result. I’ll write a bit more about some of the details over the next while—the typography, the offline functionality, print styles, and stuff like that. In the meantime, I hope you’ll peruse this little book at your leisure…

Resilient Web Design.

If you like it, please spread the word.

More Proof We Don't Control Our Web Pages

I’ve talked about this before: As web designers, we can’t trust the network. Sure, we have to contend with mobile data “dead zones” and dropped connections as our users move about throughout the day, but there’s a lot more to the network that’s beyond our control.

Here’s a roundup of some of my “favorite” network issue related headlines from the last few years:

Some of these issues can be avoided by serving content over HTTPS, but that still won’t enable you to bypass things like firewall blacklists (which led to the jQuery outage on Sky). Your best bet is to design defensively and make sure your users can still accomplish their goals on your site when some resources are missing or markup is altered.

We can’t control what happens to us in this world, we can only control our reaction to it.

You Can't Detect A Touchscreen

Whatever you may think, it currently isn’t possible to reliably detect whether or not the current device has a touchscreen, from within the browser.

And it may be a long time before you can.

Let me explain why…

Boxed in

The browser environment is a sandbox. Your app’s code can only get at things the browser wants you to, in order to limit the damage a malicious website can cause.

This means that the only information about the system you can get is what the browser exposes to you, in the form of HTML, CSS and JavaScript APIs. To determine if a system supports a certain feature, we can a) see if a certain API is present, or b) see if it actually does the right thing.

Historically, two browser features have been used for “touchscreen detection”: media queries and touch APIs. But these are far from foolproof.

Walk with me.

Device width media queries

Mobiles have small screens and mobiles have touchscreens, so small screen equals touchscreen, right?

[…]

So, so very wrong. Large tablets and touchscreen laptops/desktops have clearly proven this wrong. Plus thousands of older mobile handset models had small non-touch screens. Unfortunately, sites applying the mantra “If it’s a small screen, it’s touch; if it’s a big screen, it’s mouse-driven” are now everywhere, leaving tablet and hybrid users with a rubbish experience.

Touch APIs

[…]

If the browser supports events like touchstart (or other events in the Touch Events API spec) it must be a touchscreen device, right?

[…]

Well, maybe. The problem is, no one ever said that a non-touch device can’t implement touch APIs, or at least have the event handlers in the DOM.

Chrome 24.0 shipped with these APIs always-on, so that they could start supporting touchscreens without having separate “touch” and “non-touch” builds. But loads of developers had already used detects like the example above, so it broke a lot of sites. The Chrome team “fixed” this with an update, which only enables touch APIs if a touch-capable input device is detected on start-up.

So we’re all good, right?

Not quite.

An API for an API

The browser is still quite a long way from the device itself. It only has access to the devices via the operating system, which has it’s own APIs for letting the browser know what devices are connected.

While these APIs appear to be fairly reliable for the most part, we recently came across cases whereby they’d give incorrect results in Chrome on Windows 8… they were reporting presence of a touchscreen (“digitizer”), when no touchscreen was connected.

Firefox also does some kind of similar switching and it appears to fail in the same cases as Chrome, so it looks like it might use the same cues – although I can’t profess to know for sure.

It appears certain settings and services can mess with the results these APIs give. I’ve only seen this in Windows 8 so far, but theoretically it could happen on any operating system.

Some versions of BlackBerry OS have also been known to leave the touch APIs permanently enabled on non-touch devices too.

So it looks like the browser doesn’t know with 100% confidence either. If the browser doesn’t know, how can our app know?

Drawing a blank

Assuming the presence of one of these touch APIs did mean the device had a touchscreen… does that mean that if such a touch API isn’t present then there definitely isn’t a touchscreen?

Of course not. The original iPhone (released in 2007) was the first device to support Touch Events, but touchscreens have been around in one form or another since the 1970s. Even recently, Nokia’s Symbian browser didn’t support touch events until version 8.2 was released last year.

IE 10 offers the (arguably superior) Pointer Events API on touch devices instead of the Touch Events spec, so would return false for the ontouchstart test.

[…]

Neither Safari nor Opera has implemented either touch API in their desktop browsers yet, so they’ll draw a blank on touch devices too.

Without dedicated touch APIs, browsers just emulate mouse events… so there are loads of devices kicking about with touchscreens which you simply can’t detect using this kind of detection.

[…]

You’re doing it wrong

In my opinion, if you’re trying to “detect a touchscreen” in the first place, you’re probably making some dangerous assumptions.

[…]

So what should I do?

For layouts, assume everyone has a touchscreen. Mouse users can use large UI controls much more easily than touch users can use small ones. The same goes for hover states.

For events and interactions, assume anyone may have a touchscreen. Implement keyboard, mouse and touch interactions alongside each other, ensuring none block each other.