Dennis Schubert

WebCompat Tale: Touching Clickable Things

2021-08-20 2021-08-25 mozilla, webcompat

Did you know your finger is larger than one pixel? I mean, sure, your physical finger should always be larger than one pixel, unless your screen has a really low resolution. But did you know that when using Firefox for Android, your finger is actually 6x7 millimeters large? Now you do!

Unlike a pixel-perfect input device like a mouse or even a laptop’s trackpad, your finger is weird. Not only is it all soft and squishy, it also actively obstructs your view when touching things on the screen. When you use a web browser and want to click on a link, it is surprisingly difficult to hit it accurately with the center of your fingertip, which is what your touchscreen driver sends to the browser. To help you out, your friendly Firefox for Android helps you out by slightly enlarging the “touch point”.

Usually, this works fine and is completely transparent to users. Sometimes, however, it breaks things.

Here is an example of a clever CSS-only implementation of a menu with collapsible sub-navigation that I extracted from an actual Web Compatibility bug report I looked at earlier. Please do not actually use this, this is broken by design to make a point. :) Purely visual CSS declarations have been omitted for brevity.

Source:

<style>
  #menu-demo li ul {
    display: none;
  }

  #menu-demo li:hover ul {
    display: block;
  }
</style>
<section id="menu-demo">
  <ul>
    <li><a href="#menu-demo">One</a></li>
    <li>
      <span>Two with Subnav</span>
      <ul>
        <li><a href="#menu-demo">Two &gt; One</a></li>
        <li><a href="#menu-demo">Two &gt; Two</a></li>
      </ul>
    </li>
    <li><a href="#menu-demo">Three</a></li>
  </ul>
</section>

Result:

Now, just imagine that on Desktop, this is a horizontal menu and not a vertical list, but I’m too lazy to write media queries right now. It works fine on Desktop. However, if you try this in Firefox for Android, you will find that it’s pretty much impossible to select the second entry, and you will just hit “One” or “Three” most of the time.

To understand what’s going on here, we have to talk about two things: the larger “finger radius” I explained earlier, and the rules by which Firefox detects the element the user probably wanted to click on.

Touch Point expansion

The current touch point expansion settings, as set by the ui.mouse.radius.* preferences in about:config, are: 5mm to the top; 3mm to the left; 3mm to the right; 2mm to the bottom. There probably is a good reason why the top/bottom expansion is asymmetric, and I assume this has something to do with viewing angles or how your finger is shaped, but I actually don’t know.

To visualize this, I prepared a little annotated screenshot of how this “looks like” on my testing Android device:

A screenshot of the live menu demo from above. A red dot in the middle of "Two with Subnav" marks the position where the user placed the middle of their finger, a blue border marks the outline of the area Firefox considers "touched". The blue outline spans well into the "One" menu item.

The red dot marks the center of the touch point, the blue outline marks the area as expanded by Firefox for Android. As you can see, the expanded touch area covers part of the previous menu item, “One”. If you’d try to touch lower on the item, then the bottom expansion will start to cover parts of the “Three” item. In this example, you have a 9px window to actually hit “Two with Subnav”. On my device, that’s roughly 0.9mm. Good luck with that!

With this expansion in mind, you might wonder why you’re not hitting the wrong items all the time. Fair question.

”Clickable” elements

Firefox doesn’t just click on every element inside this expanded area. Instead, Firefox tries to find the “clickable element closest to the original touch point”. If all three <li>s contained links, then this wouldn’t be an issue: links are clickable elements, and “Two with Subnav” would, without a doubt, be the closest. However, in this example, it’s not a link, and then the rules are a little bit more complicated.

Things Firefox considers “clickable” for the purpose of finding the right element:

  • <a>s.
  • <button>, <input>, <select>, <textarea>, <label>, and <iframe>.
  • Elements with JavaScript listeners for:
    • click, mousedown, mouseup
    • touchstart, touchend
    • pointerdown, pointerup
  • Elements with contenteditable="true".
  • Elements with role="button".
  • Elements with cursor: pointer assigned via CSS.

Unfortunately, none of the rules above are true for the “Two with Subnav” element in the example above. And this means that the “closest clickable element” to the touch point here is, well, “One”. And so, Firefox dispatches the click event to that one.

Matching any of the conditions, even simply changing the cursor via CSS, would provide the browser with enough context to do “the right thing” here.

Conclusion

This issue, once again, is one of those cases where I do not yet have a satisfying outcome. I wrote a message to the site’s authors, but given the site is based on a Joomla template from 2013, I do not have high hopes here. As for changes inside Firefox, we could treat elements with :hover styling and mouseover listeners as “clickable”, and I filed a bug to suggest as much, but I’m not yet convinced this is the right thing to do. From what I can tell, neither Chrome nor Safari do a similar expansion, so just dropping it from Firefox is another idea. But I kinda like the way it makes things better 99.9% of the time.

In any case, this serves as yet another reminder of why having semantically correct markup is important. Not only do attributes like role="button" on clickable elements help out anyone relying on accessibility features and tooling, browsers also depend on these kinds of hints. Use the tools you have, there’s a reason why the role attribute is part of the web. :)

Update from 2021-08-25

Good news! The developer responsible for the site has responded, and they did fix the issue by adding role="button" to the links. Huge success!