A few sneak peeks into Hey.com technology (II - Keyboard shortcuts)
Tags: rails, ruby, webdev, javascript
Published: 2020-06-19 17:18:50

Let’s start with a nice little feature of Hey, the **keyboard shortcuts**. Every Hey page has a few shortcuts defined that allow you to do the common tasks without ever touching your mouse. You can learn the keystrokes from a listing that will pop up by pressing **`"?"`** or clicking the little keyboard icon in the bottom-right corner of each page.

![Hey keyboard shortcuts popup menu](https://dev-to-uploads.s3.amazonaws.com/i/3sqqbldsm372rxqdx6ay.png)

Basically, there are two types of shortcuts in Hey: one will just take your browser to another page (as if you clicked a regular `<a href="...">` link), the other one will trigger some custom JavaScript action, making the current page interactive.

OK, how do they work? Let’s dive into the source code!

### Page transition shortcuts

When we open the Page inspector on the Imbox page (which, as we know, [is not a typo](http://itsnotatypo.com)), we’ll find several `<a>` tags that seem to be relevant for keyboard shortcuts, right near the top of the HTML body.

![Page transition shortcuts](https://dev-to-uploads.s3.amazonaws.com/i/s6u4s1l0izli85s16n9s.png)

Let’s dissect one of them. As you can see, it’s just a normal HTML link, nothing special:

```html
<a data-controller="hotkey" data-hotkey="2" href="/feedbox" hidden="hidden">
  The Feed
</a>
```

* It has its **URL** (`href`) that leads to the Feedbox page. 
* It also has a **content**, “The feed”, but it’s not shown on the page because of the **`hidden` attribute**. This is a [global attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) serving just this particular purpose - including things in the DOM but not showing them. Thus, the tag sits silently in the page, unseen.
* Now, the **`data-controller` attribute** is important and is probably familiar to anyone dealing with recent versions of Rails: it’s a pointer to the corresponding **Stimulus controller** and the value suggests that this controller has something to do with  hotkeys.
* Finally, the **`data-hotkey` attribute** must be the particular key stroke definition of this keyboard shortcut, meaning that pressing the `"2"` key will load the Feedbox page.

> BTW, I find it notable that even if we don’t have a clue how these link internals are wired together, these little tags, these single lines of HTML code still **state their purpose very well**. They define the shortcuts and it’s immediately obvious what key leads to what page! This is the cleanness and focus on readability that I like about Rails. In a Rails template, you would write such link somehow like this (or, even better, you would create a `hotkey_link` helper method):

```ruby
link_to "The feed", "/feebox", data: { controller: "hotkey", hotkey: "2" }, 
                               hidden: true
```

To further understand how the hotkey link works, we have to say something about **[Stimulus](https://stimulusjs.org)**. It is a small and highly opinionated JavaScript framework that allows you to get rid of JS “sprinkles” in the templates, move all JS code out to separate “controller” files and link them to the page in a very clean and readable way - via HTML attributes. If you haven’t met Stimulus yet, give it a few minutes and read through the [Stimulus Handbook](https://stimulusjs.org/handbook/introduction) and you’ll grasp everything needed to go on here.

The `data-controller` link attribute directs us to the "hotkey" Stimulus controller. Let’s have a look at it then! We’ll switch to the Debugger tab in the Developer tools and expand the Webpack ⟶ `app/javascript` ⟶ `controllers` path to find and open the `hotkey_controller.js`:

![The hotkey_controller code](https://dev-to-uploads.s3.amazonaws.com/i/x2e4io3rzq4z2lglv535.png)

We won’t pretend to understand everything but one thing jumps out right away: the code imports two functions, `install` and `uninstall`, from the [`@github/hotkey` library](https://github.com/github/hotkey) and …there is even a link to it in the comment… so let’s [peek in](https://github.com/github/hotkey):

![Github hotkey library](https://dev-to-uploads.s3.amazonaws.com/i/bkv0jarn3msqdjis8ipk.png)

And _voilá!_ The Github library obviously does all the low-level hardcore stuff that is needed to set up a hotkey on a page. All it needs is an element on the page with a `data-hotkey` attribute and when someone presses the given key, it virtually **clicks the element**. Given that our element is an `<a>` tag, clicking it will simply visit the target page from the `href` attribute.

For all this to work, you need to call the `install` method from the library, giving the to-be-clicked element as an argument. That is precisely what is being done in the `connect` method of the Stimulus controller. The [`connect` method is called](https://stimulusjs.org/reference/lifecycle-callbacks#connection) whenever the corresponding HTML element (the `<a>` tag in our case) is added to the DOM, i.e. just after loading the web page.

I think the life cycle of a page keyboard shortcut is complete now:

- Installing the shortcut:

  - As the page loads, the Stimulus framework finds the `data-controller` attribute in the “hotkey links”.
  - Stimulus creates a new instance of the `hotkey_controller` and calls its `initialize` and `connect` methods. 
  - The `connect` method calls the `install` method from the Github hotkey library which will actually add the proper low level JS event handler for the key press.

- When the key is pressed:

  - The low-level (`keydown`) event is triggered by the browser.
  - This is caught by the Github library event handler.
  - This handler virtually “clicks” the original element, i.e. the "hotkey" link.
  - The browser visits the URL in the link.

### Generic action shortcuts

What if we wanted to call a custom JS function instead of visiting a page upon our hotkey press? Firstly, we probably should not use an `<a>` element for our hotkey definition, so that the browser won’t try to visit another page upon “clicking” it.

Next, we can use the [Stimulus convention](https://stimulusjs.org/reference/actions) to tell the browser what action to call upon the click event in our element.

We can find an example in the Hey source code:

![Hotkey definition with custom JS action](https://dev-to-uploads.s3.amazonaws.com/i/ysqh8ipcjc9rzqq1bpgl.png)

This time it’s a `button` element and it has only one particular difference from the "hotkey link": it defines the **`data-action` attribute**: 

```html
<button ... data-action="bulk-actions#select">...</button>
```

This is a short form of an expression which has the following equivalent meaning:

```html
<button ... data-action="click->bulk-actions#select">...</button>
```

In the Stimulus convention, the [`data-action` attribute](https://stimulusjs.org/reference/actions) tells the Stimulus framework to watch for the JS event given before the `->` and when the event triggers, call the Stimulus controller action given after the `->`. If the event name is not specified, it [defaults to the `click` event](https://stimulusjs.org/reference/actions#event-shorthand) for the button element.

Thus, in our case the `select` action from the `bulk_actions_controller.js` will be called upon "clicking" this hidden button, i.e. upon pressing the `"x"` key on your keyboard. Neat, isn’t it?

### Summary

With the convenient usage of the Github hotkeys library, the web page code can be kept very clean and concise. And it is the Stimulus framework that makes the code very flexible, without cluttering the HTML page code itself. The HTML page stays short and clean and the JS code is bundled elsewhere and nicely cached. The Stimulus convention makes the whole process readable and predictable which is always a good thing!

_Thanks for reading, stay tuned for more Hey tech goodness! Next time I’ll dig into a great new feature − [automatic updating of custom page areas](https://dev.to/borama/a-few-sneak-peeks-into-hey-com-technology-iii-turbolinks-frames-5e4a) from the server!_