A11y Jump Menu - your secret ally for navigating long pages.

October 10, 2013

Last Fall, I was giving a talk here at LinkedIn about accessibility. I ended the talk with the presentation of a very rough prototype for a tool that would allow keyboard users to more easily navigate some of the very long content pages found on the LinkedIn site. This is especially challenging for these users, because they navigate page content using the tab key to jump from anchor to anchor and form field to form field. Imagine having to hit the tab key several hundred times just to get through a profile page.

The idea came from some brainstorming I’d done with a former colleague, Victor Tsaran (now Senior Accessibility Program Manager at PayPal) , and a talk on “Accessibility as Innovation” given by Karl Groves and Elle Waters of Simply Accessible. After demonstrating my early prototype, I asked for some design and and programming help to make it special for Hack Day. So, I was joined by Moses Ting (visual design), Sean Sands (interaction design), Dylan Hudson (web developer) and Jo Chou (web developer tester) to more fully develop the idea. We ended up as the November 2012 Hack Day “Best in Show” for the A11y Jump Menu. The journey then began to make the code production-ready for release to the entire site.

How it works:

Viewing my profile with cursor in location bar.

When a user approaches the site using a keyboard, they will start with their cursor in the location bar and then hit tab until they enter the browser window (some operating systems require this functionality to be turned on specifically in settings). We’ve placed a “skip navigation” link as the first link on the page, which is hidden from view and allows screen reader users to click to move past the navigation to the body of the page using a traditional named anchor link. However, if JavaScript is enabled, the hidden link becomes the trigger for the entire functionality. When the user focuses on that link using the tab key, the script will “harvest” H2 and H3 tags from the page DOM and place them in a special menu.

User places focus on the first link and the jump menu renders with focus on the top toolbar element.

When a user clicks on one of the header items in the menu, their cursor focus will be moved to the corresponding header element on the page allowing them to continue reading/interacting from that point.

Selecting the 'Activity' landmark from the menu.

The JavaScript:

The JavaScript contains a simple constructor which establishes a few instance variables for the elements that control the interaction and runs an initialization method, which is public and attached to the prototype (along with all the other public methods for testability). The only thing that happens in the initialization method is a single event listener looking for focus on the trigger link. All the heavy lifting for building the menu and attaching the rest of the interaction events happens after that focus event fires. This keeps any performance overhead away from mouse users who will not benefit from the tool.

Technical Challenges:

A good mix of landmarks for all pages - We initially only harvested H2 tags, but then realized that not enough interesting landmarks were available in the menu, so we expanded to H3 tags as well. However, some pages were so heavy with H3 tags that the menu would extend beyond the height of the window. We eventually settled on all H2 tags and the first 10 H3 tags. We first push all the H2s into a queue in the order they appear, followed by all the H3s. We then iterate through the harvested headers and append the associated markup template for each item.

The harvested content in the menu - We initially just cached and copied the innerHTML for each header into the menu. We ran into some issues with event listeners being attached to unique IDs in some of that innerHTML, causing issues with both validity of the rendered markup as well as the interactions coming from clicking the menu contents. Therefore, we ended up changing the method to be a slightly modified implementation of the Jquery text() method from the 1.6.1 version of the library.

Harvesting hidden headers and empty headers - We found items appearing in the menu that were not visible on the page and could not be focused. Additionally, we found some items in the menu were empty, meaning there was no inner text in the header element or any of its children. In order combat this, we had to check whether a header element, its parent or grandparent element had display, visibility or text-indent set to obscure the element before adding the markup for it to the menu - this method was contributed by Yogesh Mandawewala, a web developer on our Accessibility Task Force. And, as a final check before adding the item, we now check to see if the method returning the text of the node and its children came back with an empty string.

Scrolling behavior - as LinkedIn migrated to the new global navigation bar, we needed to adapt the interaction slightly. One change required is that the A11y Jump Menu needed to collapse if it was showing and the user started to scroll on the page. However, the scroll event fires like crazy causing a potential performance drag, so we had to attach it when the menu opened and then detach when the menu closed (which is the method called when the scroll event fires).

Status and future:

Currently the menu is available to all users with their interface setting set to English. Over the coming weeks, we plan to release the interaction to all other languages. We have some future enhancements in mind like a key command that will re-open the menu from anywhere on the page (not just tabbing from the location bar). We’d love to hear if you are a keyboard-only user and have suggestions on how to enhance or improve the interaction. Please write to us at the “Accessibility Feedback” link inside the A11y Jump Menu.

Topics