laitimes

Add shortcut keys to Zhihu

author:Flash Gene
Add shortcut keys to Zhihu

Recently, Zhihu has added some shortcuts on the desktop web:

  • When browsing pages such as the homepage, personal page, and search results page[1]: press J and K to select items in the list, press O to expand or collapse the full text, and press V and D to approve and disagree. In addition, pressing Esc can also exit most pop-up windows and pop-up menus.
  • When playing a video: press F to make the player full screen, press M to mute the video, press the numbers 0-9 to jump to the corresponding video progress (0%-90%), etc.
  • There are also some shortcuts in the editor for answers and articles: for example, press ⌘+K to insert links, etc. Move the cursor over the button in the toolbar to see the introduction.
Add shortcut keys to Zhihu

In the non-input state, press ? (Shift+/) to see a list of shortcuts

In the process of developing shortcuts, there are some lessons worth sharing.

Development shortcuts

The scope of the shortcut

It's not difficult to implement shortcuts because it's simple to listen for keyboard events. Most of the time, we only need to register the event on the outermost element that we want to respond to, and the callbacks that respond to the event (such as closing the modal) can also be implemented within this outermost component.

Add shortcut keys to Zhihu

The component structure of Zhihu homepage

But this time it's more complicated. For example, press V on the homepage to agree with the answer: the timeline entry (FeedItem) in the list includes various types of content including the AnswerItem, and the VoteButton in the answer is the best place to respond to shortcut events. It's easy to register an event on the outermost FeedItem but not to respond to the event, and in the innermost VoteButton you can respond to the event but you can't simply register the event.

It's interesting to elegantly implement child components (endorse, favorites buttons, etc.) in React's component relationships to register shortcuts and callbacks, while still being able to respond to shortcuts all the way down to the parent component (the entire answer or even the feed entry).

A simple idea is to skip registering shortcuts in the child component, register the shortcut in the parent component, and call the child component's instance method at the time of the callback. This can cause the following problems:

  • The hierarchical abstraction of components is almost abandoned: calling child component instance methods from the outside in is anti-pattern and difficult to manage (not to mention the fact that in React you can get a real component instance by skipping a HOC that plugs into a function like connect).
  • Shortcuts are defined and implemented repeatedly: for example, the same V-approved shortcut is registered and implemented in all parent components that reference the Approve button, such as ideas, articles, and answers.
  • In Zhihu's component structure, this implementation can only be carried out within the scope of AnswerItem located in the middle of the hierarchy at most, because the implementation of FeedItem does not care about the business inside the sub-component.

The solution used by Zhihu now is: based on the React context, all subcomponents share a "shortcut key instance", and all subcomponents under this "scope" will be registered on this instance when declaring shortcuts.

Refer to the pseudocode below:

function FeedItem() {
  const feedItemElement = useRef(null)
  return (
    <ShortcutContext value={feedItemElement}>
      <div className="FeedItem" ref={feedItemElement}>
        <AnswerItem />
      </div>
    </ShortcutContext>
  )
}           

In the FeedItem, a new shortcut instance is initialized with its own HTML element and set in the context, and the actual keydown and other keyboard events are registered on this element. So all subcomponents can register a new shortcut on this instance via context.

function VoteButton() {
  const handleVote = () => console.log('voting')
  // 在 useShortcut 中查找当前 context 中存在的快捷键实例,并注册在实例上
  useShortcut('v', handleVote)
  return <button onClick={handleVote}>赞同</button>
}           

In the VoteButton, you can register and respond to shortcut events just like you would a click event, which looks and writes very cleanly.

A shortcut for list navigation

Vim-style list navigation using J and K is a common shortcut design for many websites[2]. Here are some of the details you might encounter if you were to start from scratch:

  • Using the element.focus() method directly leaves it up to the browser to decide on a suitable scroll position, which is usually not expected (in the case of Zhihu, it's because there's a fixed navigation bar at the top, which native scrolling is often blocked). We disable this native scrolling with the preventScroll: true parameter and use the result of our own calculations to window.scrollTo() to the specified position.
  • Not all elements can be focused. If it's just a normal <div> element, you can set the tabIndex to 0 or -1 to make it focusable. If it is set to -1, it can only be focused, but not tabbed[3]. Whether you want to be tabbed depends on whether there are elements within the element that can be read, or whether the element itself has interactable behavior.
  • If you use the scroll wheel or touchpad to move the web page to a long distance after focusing on a list element, you need to discard the already focused elements and focus as much as possible on the elements that are visible in the view area [4], or adopt another strategy [5].
  • Unlike other similar products, most of Zhihu's list elements are not the entire area that can be clicked and entered into the detail page, but encourage the content to be expanded and consumed on the current page (otherwise it can be wrapped directly with a <a> tag). This time, I deliberately implemented the focus shortcut key for pressing the Enter key to enter the details page when the list item is listed.
  • If you want to focus on a list item because of a click (e.g. item 2), it's best to press J or K to navigate by just showing the focus outline on that element first, rather than navigating directly to item 1 or item 3. Because at this point the user doesn't necessarily know that they're focused on this list item. This is related to the fact that Zhihu does not add an outline to the focus caused by the click behavior.

Focus Outline 该在什么时候展现

When using shortcut keys to browse, it is important to "know where the cursor is". A screen reader can read the contents of the current focus element. If you don't look at the sound, you can only know what element is currently being focused on by the visual style.

Add shortcut keys to Zhihu

Chrome 为 Input 标签默认添加的 focus outline

By default, browsers will add an outline style to the focusable element through the :focus pseudo-class, because this style is not very good-looking, and the focusable element will often design some styles that respond to clicks, so the general product or design will require the engineer to cancel the style. However, if you simply cancel all focus styles, you will know "where I am" with the mouse, but you won't know "where I am" when you use the keyboard to access.

The most elegant solution is to use CSSWG's :focus-visible pseudo-class to add the focus style (while disabling the original :focus style), which is described in detail in WICG's polyfill. To put it simply, "only focus triggered by the keyboard should be styled".

Add shortcut keys to Zhihu

The focus style of the Zhihu button

Zhihu achieves this design with a similar idea to the polyfill: after using the keyboard, the <html> data-focus-visible attribute is added to the element. Each element will only have a focus style added if the attribute is included. And Zhihu has also modified the default style to make it more beautiful. You can use the Tab key to tab over the Approve button to see the effect.

When developing this part of the feature, there is also a special design: when using a shortcut key such as ⌘+C for copy-paste operations (to be precise, the keys include Modifier keys such as Control or Shift), it is not considered a normal keyboard operation, and it does not show the focus outline, otherwise the outline will come and go too often. Interestingly, Twitter has actually done something similar, giving the impression that engineers on both sides of the ocean are working on the user experience...

The relationship between shortcuts and accessibility

Visually impaired users are involved in a lot of discussions about shortcuts, because they use screen readers to browse the web and have to use various keyboard shortcuts, including the Tab key, to locate and operate the cursor. However, support for visually impaired users goes far beyond adding shortcuts.

@devil缠

In the comments section of an answer [6] it is mentioned:

If it's just a single key, it's basically useless. Take V as an example: when you reach the "Agree" button with Tab, you can click "Agree" directly by pressing the spacebar. Also, if you click V in the view content area, the focus will not run to the "Agree" button.

Reasonable shortcuts may not be very useful, and unreasonable shortcuts will not only not help visually impaired users, but will also be unhelpful.

@殷晓波

and

@devil缠

It is mentioned that "after using V to agree, I hope to be able to focus on the agree button", and if you don't actually use a screen reader to browse the web, you can't imagine the reason for this sentence:

Add shortcut keys to Zhihu

After the shortcut is approved, you need to shift the focus

To put it simply, a screen reader will only read the text in focus when the focus changes, and it won't be able to monitor design feedback that can be easily understood by the average person, such as the "approve button turns dark blue". If you use the shortcut to approve without changing the focus, you won't even know what happened when you press the keyboard, and you won't know that you have already agreed with the answer. At this point, it is strange to tab over the Agree button and read the text "Approved" to know what happened.

The browser's clicking behavior automatically focuses on interactable elements (e.g <button> . or ), <a>and pressing a shortcut such as Enter or Space can "simulate a click", which is an off-the-shelf system that is easy to ignore. In the actual experience, it is not much more troublesome to tab to the "Read More" button and then press Space to expand the full text than to use the shortcut key O to expand the full text.

In addition, many screen-reading software or visually impaired users also define and develop personalized shortcuts [6]. In this way, using <button> semantic tags such as so that elements can be interacted with can also be focused, using as much as possible <a> instead of using location.href for page redirection when listening for onClick events, configuring aria attributes, etc... It makes more sense for Accessibility.

In general, the implementation of shortcut keys should pay attention to the use and interaction experience of visually impaired users as well as other functions, such as:

  • After any shortcut action, shift focus as if you clicked.
  • Once you've shifted focus, you'll also need to configure attributes such as aria-label to read meaningful cue text, such as yes or yes, disagree or disagree.

Shortcuts are only one part of accessibility, and accessibility is a much more systematic and complex project. Zhihu has made some efforts [7], but it is not enough. Suggestions from friends who know more about this field are also welcome.

Q&A

Can I turn off shortcuts?

Add shortcut keys to Zhihu

Yes. If you use a browser extension such as Vimperator or Vimium to customize the shortcut and don't want to conflict with Zhihu, you can turn off the shortcut in your personal preferences (https://www.zhihu.com/settings/preference) on the desktop web page[8].

How do the notes and references for this article work?

This is a new feature of the editor and will be introduced when it becomes available.

reference

^These shortcuts were not synchronized when migrating to a new version of the web page and were not implemented for a long time.

^Including Twitter, Facebook, Gmail, Sina Weibo, etc., Zhihu has benefited greatly from the implementation details of these websites.

^The difference between the two is explained in a library called tabbable, which is useful for implementing effects such as focus traps. https://github.com/davidtheclark/tabbable

^How to efficiently find the element closest to the viewport and the smallest scroll distance, this algorithm is more interesting, so I won't repeat it here.

^Zhihu and Facebook, like Sina Weibo, will select new elements visible in view, while Twitter will give up scrolling.

^abAccording to @devil in the comments section of this answer, the screen reading environment he uses also defines shortcut https://www.zhihu.com/question/19842222/answer/17152043 such as K to jump down 10 links, Shift+K to jump up to 10 links, etc

^@长天之云 的答案介绍了一些知乎对 a11y 的支持 https://www.zhihu.com/question/20487917/answer/15265930

^Available only in the browser you are currently using.

Author: Sun Beiji

Source: https://zhuanlan.zhihu.com/p/59928288