Redesign module selector - Fast & Fuzzy
The typeahead field for the module dropdown menu is now faster, fuzzier, and fully keyboard accessible!
Faster startup
Matthew Beale noticed that on test suites with 800 modules, test startup was sometimes delayed by ~5 seconds in Chrome 95.
In previous QUnit versions, we eagerly rendered the dropdown menu with options for all module names. (The menu is only shown on focus.) The JavaScript code for this only takes about 5-10ms (0.005 seconds), even on very large projects. But, every so often there is an unexplained slow task right after this function. This performance issue did not affect Firefox and Safari.
Whatever the cause may be, we’ve cut this cost by lazily rendering the menu when the module field is first focussed.
Instant typeahead
We previously applied a 200 ms input debounce on filtering the dropdown menu. It seems a lot people type just fast enough for the menu to sit idle until you stop typing. This provides a subpar user experience, because the you won’t know if what you typed will find what you’re looking for, until you stop typing.
The module selector in QUnit is powered by the super fast fuzzysort.js library. Fuzzysearch actually takes only ~1ms, so it should be able to keep up in real-time, even for projects with hundreds of QUnit modules defined. I considered removing this debounce entirely, but that risks causing a different kind of lag instead.
The new design lowers the debounce to a delay of 0 ms.[1] The module selector now feels smooth as butter, with an instant response on every key stroke.
What’s the difference between a 0 ms debounce, and no debounce?
Consider what happens if you type faster than the browser rendering can keep up with. For example, rendering may take longer in some cases. The event handler will be running and, meanwhile, another character is typed.
Without a debounce, these keyboard events will pile up. Each one will be honoured and diligently played back-to-back and in order. Each event callback will begin its rendering long after other keystrokes were already sent. It will feel akin to acting on a remote server over bad WiFi, with an ever-growing backlog of unprocessed input events.
With 0 ms debounce, we queue up at most 1 render callback, and that “next” render will always be for the then-current value of the input field. Another way to look at it: It is as if, whenever we finish a rendering, we will cancel all-except-the-last pending callbacks.
For the common case where rendering is quick enough to keep up with keystrokes, both ways improve what we had before. Both ways will render results immediately on every keystroke, with no delay. Check Debounce demo on CodePen to experience the difference.
Accessibility
The redesign includes various usability and accessibility improvements for the module dropdown menu.
Highlights:
- Currently selected choices are now hoisted to the top and always visible, even if not matched by the current filter.
- There is no longer a “dead” tab target between the action buttons and the first menu option (see below animation).
- The focus ring for the “Apply” button is no longer clipped on two sides by overflow (see below animation).
- More breathable design for options and buttons. Toolbar buttons have a solid outline and no longer lost in a sea of greyness.
Love The Fuzz
In fuzzysort.js, each result is internally ranked on a range from several thousands points below zero (worst) upto 1.0 (perfect match).
One of the Fuzzysort features is the “threshold” option, which omits results below a certain score. We previously had this to -1000
, which sounds like it should let most results through.
In practice, it corresponded to tolerating a few missing letters in the first word. For example, suort for promise
did find support for promise
. But, support for pomise
already yielded zero results, despite only missing one letter!
This is counter-intuitive and contrary to how fuzzy search works in text editors. For example, in Sublime Text, all files of which you have typed a subset of the name, are included. It is only when you type a character that isn’t in an entry’s name, that it is removed from the options.
In this redesign, I’ve disabled the “threshold”, which achieves the desired effect.
See also
- HTML Reporter: Faster startup and improved usability of module filter · Issue #1664
- HTML Reporter: Faster and fuzzier module dropdown · Pull Request #1685
- QUnit 2.18.2 Release
- Blog: Introduce mult-select module picker, April 2016.
Footnotes:
-
Note that timers from setTimeout have a minimum delay of 4ms in practice, which is close enough to zero. ↩