Use Vim Keyboard Shortcuts on your Blog

Use Vim Keyboard Shortcuts on your Blog

⌨️ Adding Vim Keyboard Navigation to you Blog Posts

Let's look at how to use Vim keyboard shortcuts on your blog. Although the Vim text editor first appeared in the late 20th century, it is still enjoys some popularity. The Stackoverflow 2021 Devleoper Survey results show 24% of developers use Vim and another 5%, Neovim (a younger alternative). I personally use Vim for dev work and end up finding myself trying to use Vim keyboard shortcuts outside of the editors towards the end of long days spent coding! Completely selfishly, why not make life easier for Vim developers to get around your blog posts?

In this post we see how you can modify a Svelte blog site to respond to some Vim keyboard shortcuts. We'll clone the SvelteKit Blog MDsveX Starter to get things going quicker. Then we will create a new component for responding to keyboard shortcuts and add that to the existing blog post template. If you are not yet familiar with SvelteKit, you can still follow along. If you don't like Svelte then the code can also be adapted for use on React or plain HTML/JavaScript sites. Why don't we press on?

🔑 Start your Engines!

Let's start by cloning the starter and spinning up our development server:

git clone https://github.com/rodneylab/sveltekit-blog-mdx.git vim-shortcut-blog
cd vim-shortcut-blog
pnpm install
cp .env.EXAMPLE .env
npm run dev

If this is your first time using the stater, have a poke around the folders and then jump to localhost:3000 to get used to it. If you already have something running on TCP port 3000 check out the post on getting started with SvelteKit to see how to switch ports.

🧱 Use Vim Keyboard Shortcuts on your Blog: Building our Site

Let's create the VimKeyboardShortcuts component. Although we will create it as a Svelte component, it will not render anything, just react to key presses. Paste this code into a new file src/lib/components/VimKeyboardShortcuts.svelte:

<script>
  import { onDestroy, onMount } from 'svelte';
  import { browser } from '$app/env';

  onMount(() => {
    if (browser) {
      document.addEventListener('keydown', handleKeyDown);
    }
  });

  onDestroy(() => {
    if (browser) {
      document.removeEventListener('keydown', handleKeyDown);
    }
  });

  let gAlreadyPressed = false;

  function viewportHeight() {
    return window.innerHeight;
  }

  function handleKeyDown(event) {
  }
</script>

This is just a shell, we will flesh it out soon. This shell let's us see a few Svelte features. In line 7 we add a an event listener, which is what allows us to detect that a key has been pressed. Event listeners run in the background until they are removed. So to use it in Svelte, we add the event listener when our component is created and then remove it when the component is destroyed (e.g. user navigates to another page). In Svelte the onMount and onDestroy functions let us run code when the component is created and destructed. Here we only need to listen for the keydown event but you can listen for other events, just adding additional addEventListener lines. Note the event listener calls our handleKeyDown function which we are yet to fill out.

We will use gAlreadyPressed to listen for a repeat press of the g key (as in the gg shortcut which takes us to the top of the page). Finally we have a utility function to calculate the viewport height. This is the inner window browser window height. We will use this to scroll up or down by half a screen.

That's the shell. Before we flesh it out, let's add the component to our blog post template.

Integrating VimKeyboardShortcuts

Update the BlogPost component (src/lib/components/BlogPost.svelte) as below. This is the template used to render blog posts

<script>
  import readingTime from 'reading-time';
  import BannerImage from '$lib/components/BannerImage.svelte';
  import SEO from '$lib/components/SEO/index.svelte';
  import VimKeyboardShortcuts from '$lib/components/VimKeyboardShortcuts.svelte';

  export let imageData;
<SEO
  article
  {breadcrumbs}
  {slug}
  {title}
  {datePublished}
  {lastUpdated}
  {metadescription}
  {timeToRead}
  featuredImage={featuredImageObject}
  ogImage={ogImageObject}
  ogSquareImage={ogSquareImageObject}
  twitterImage={twitterImageObject}
/>
<VimKeyboardShortcuts />
<BannerImage {imageData} />
<h1>{title}</h1>

Now we have the component in the DOM for our blog posts, we just need to fill it out and test it.

handleKeyDown

Let's jump back to src/lib/components/VimKeyboardShortcuts.svelte and add the code for handling key presses:

      function handleKeyDown(event) {
    const { ctrlKey, key } = event;
    switch (key) {
      case 'd':
        if (ctrlKey) {
          window.scrollBy(0, 0.5 * viewportHeight());
        }
        break;
      case 'G':
        const lastElement = document.getElementsByTagName('main')[0].lastElementChild;
        lastElement.scrollIntoView(true);
        break;
      case 'g':
        if (gAlreadyPressed) {
          const firstElement = document.getElementsByTagName('main')[0].firstElementChild;
          firstElement.scrollIntoView(true);
        } else {
          gAlreadyPressed = true;
          setTimeout(() => {
            gAlreadyPressed = false;
          }, 1000);
        }
        break;
      case 'u':
        if (ctrlKey) {
          window.scrollBy(0, -0.5 * viewportHeight());
        }
      default:
    }
  }
</script>

We use a switch statement here in the logic for responding to different key presses. This is a little cleaner than a series of if statements. switch is one of those things you might look at when initially learning JavaScript, but then not have much use for in you domain. So here's the MDN docs on switch in case you need to brush up. Switch cases use strict comparison (===).

Handling a Key Double Press

Let's look at the g block in detail (lines 3545). This block handles the shortcut which scrolls to the top of the post. It is triggered when the user presses g twice. The first thing we do in the block is check if g has already been pressed. If it has, we look for the main HTML element (we rely on the post using semantic HTML here). Next, we find the first child element of the main element. Then finally in line 38, we scroll that element into view.

We get smooth scrolling behaviour because the starter's global CSS includes scroll-behavior: smooth; on the html element. You can see this in src/lib/styles/styless.scss.

If the user is pressing the g key for the first time. We need to register it, but not do anything else. We can do that by setting gAlreadyPressed to true. However, we really should expect the user to press g, for the second time, quite soon after the first time (that's if they intended to execute the shortcut we are listening for). This means we need to reset gAlreadyPressed back to false after, let's say one second. This is what we do in lines 4143, using setTimeout. Our setTimeout call waits for 1000 milliseconds before it does anything. It then executes the anonymous function () => { gAlreadyPressed = false; } which we passed in as the first argument.

That's all the logic in now! The other switch are are simpler variations of the one we just went through. Next we can test it all out.

💯 Use Vim Keyboard Shortcuts on your Blog: Testing Everything

Here's a list of the keyboard shortcuts we programmed:

Ctrl + d
scroll down half a window height,
G
scroll to bottom of the post,
g g
scroll to top of the post,
Ctrl + u
scroll up half a window height.

Try them out to check all is well.

🙌🏽 Use Vim Keyboard Shortcuts on your Blog: What we Learned

In this post we learned:

  • how to listen for keyboard events in Svelte,

  • listening for key combination presses in JavaScript,

  • how to listen for a double key press in JavaScript.

I do hope there is at least one thing in this article which you can use in your work or a side project. For extensions, you could add shortcuts to jump to the next post if the user presses l or, previous post for h. On top you can jump up a level (based on the page breadcrumbs) if the user types :q. The sky is the limit, you can really go to town on this! For more Vim keyboard shortcuts Richard Torruellas has a fantastic cheatsheet. For more on Keyboard events, Tapas Adhikary wrote a tremendous freeCodeCamp post.

As always get in touch with feedback if I have missed a trick somewhere! Also, I'm especially keen to hear feedback from users with accessibility needs, as to whether adding these shortcuts has a negative impact on their experience.

You can see the full code for this Vim keyboard shortcut blog site on the Rodney Lab Git Hub repo.

🙏🏽 Use Vim Keyboard Shortcuts on your Blog: Feedback

Have you found the post useful? Do you have your own methods for solving this problem? Let me know your solution. Would you like to see posts on another topic instead? Get in touch with ideas for new posts. Also if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, please consider supporting me through Buy me a Coffee.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter and also askRodney on Telegram. Also, see further ways to get in touch with Rodney Lab. I post regularly on SvelteKit as well as other topics. Also subscribe to the newsletter to keep up-to-date with our latest projects.

Did you find this article valuable?

Support Ask Rodney by becoming a sponsor. Any amount is appreciated!