Back to Basics: Skip to Main Content Links

One of the earliest solutions in all of web accessibility was the "skip to main content" link, a same page link at the top of the page that enables users to skip past navigation links and go straight to the main content of the web page. Despite the fact that these links have been in use since the early 1990's, they actually aren't implemented well at all by browsers. They can be a huge benefit to sighted non-mousers (navigating by keyboard) but they don't work for these users in any browser other than Firefox and Internet Explorer, and they don't even work in IE unless link targets have tabindex="0". This blog post discusses all of this, and ultimately proposes a simple JQuery solution that makes same-page links work beautifully in all browsers.

People tend to think of screen reader users as being the primary beneficiary of these links. However, they're also beneficial - perhaps even more beneficial - for sighted non-mousers, who are navigating through links on the page using the keyboard (mostly commonly, the tab key). Imagine a link smack dab in the middle of the page that a keyboard user wants to select. They can see it, but they can't get to it without first tabbing laboriously through dozens, maybe hundreds, of links that comprise the site's various navigation menus.

Users of screen magnification tools such as ZoomText and Magic also benefit from these links. If their screen is zoomed significantly, they typically have to scroll down and right, hunting for the main content. If the first think they see in the upper left corner is a skip link, they can select that and find their target instantly.

Are skip nav links still necessary?

Times are changing. We now have better ways to facilitate navigation on a web page, most notably HTML headings and ARIA landmarks. Take this example:

  <div role="main">
    <h1>Main Content</h1>
    <!-- main content of the page -->
  </div>

With this markup, screen reader users can jump directly to the top-level heading (<h1>) using a hot key (for example, "h" or "1" in JAWS) or they can jump directly to the "main landmark" (role="main") with another hotkey, which in JAWS is the semicolon (;). Either way, the user agent (i.e., screen reader) is providing a mechanism for navigating the web page using the page's existing standard markup. The web developer doesn't have to add a link for this to happen.

Unfortunately, sighted non-mousers aren't likely to be using screen readers, so they're depending on browsers providing this same sort of functionality, and currently most browsers aren't doing that. Opera is the lone exception. Tapping the S key moves forward through HTML headings, and the W key moves backward. This feature was enabled by default prior to Opera 9.5. Since then, this and other single-key shortcuts must be enabled within Settings > Preferences > Advanced > Shortcuts. Other browsers have extensions that might help users to navigate headings by keyboard, but often these extensions are out of date or for various other reasons not usable for keyboard users. And no browser, not even Opera, currently provides keyboard shortcuts for landmarks or their corresponding HTML5 section elements (e.g., header, footer, article, section, nav, and aside).

Until browsers provide better support for headings, landmarks, and/or HTML5 section elements, it's still advisable to provide a "Skip to Main Content" link in the upper left corner.

Visible or hidden?

Many web sites that include skip links often position them off-screen using CSS, so they aren't visible to sighted users:

  a.hidden {
    position: absolute;
    left: -999px;
  }

Screen reader users can still access them if they're positioned off-screen. For sighted keyboard users, the link can be made visible as soon as the user hits the tab key that first time:

  a.hidden:focus { 
    left: 5px;
  }

This technique helps to avoid the problem of having links on the page that the majority of users don't understand, a usability problem that's compounded when the main content is already visible on the page, in which case the web page typically doesn't change its appearance in any way after a user clicks the skip link.

Unfortunately if the skip link is hidden, magnification users (who are likely to be using a mouse, not keyboard) won't know it's there. They can still hunt for the main content, so the page isn't totally inaccessible to them, but the page could be more usable for them with a visible skip link.

Also, in Opera (tested through 11.6) hidden links don't receive focus. Using Opera's keyboard model, one can navigate through links on a page using Shift + arrow keys, but skip links that are positioned off-screen can not be accessed.

The link target: Named anchor or id?

There are a couple of methods for identifying the target of a same-page link.

Historically, the target was typically identified with a named anchor tag:

  <h1><a name="main">Main content</a></h1>

Alternatively, the target could be an element's id attribute:

  <h1 id="main">Main content</h1>

The first method was once preferred because certain browsers (early versions of Netscape I think) didn't support linking to an id attribute. However, id attributes are much more useful in general, and by now all major browsers have supported linking to id attributes for many years and versions. In HTML5, the name attribute on an anchor element is considered obsolete, and will generate a validation warning.

Before I lay named anchors to rest though, I thought I'd better do some testing to be sure I fully understood how skip nav links are supported across browsers and screen readers. So I created a Skip Navigation Test Page.

Skip Navigation Test Results

In all browsers, a same-page link causes the page to scroll down to the point where the target content is visible on the screen. However, for most browsers this is purely a visual effect. A user's keyboard focus does not jump to the section marked by the target. If, after selecting a skip navigation link, we press tab (or shift + down arrow in Opera) to go to the next link, we find that our focus is still at the top of the page, not in the new section that we had hoped to skip to.

Firefox is the only browser that gets this right. If I select a skip nav link in Firefox, the next time I hit the tab key I will jump to the first link in the target section. This is true whether the link target is a named anchor or an id, with or without tabindex="0". (I tested with Firefox 8.0.1).

Internet Explorer behaves like all other browsers (i.e., it scrolls down to the target content but doesn't place keyboard focus there) unless the target includes tabindex="0". Then, it works fine using either a named anchor or id attribute. (I tested with IE7 and 9).

JAWS 13 works perfectly in Firefox regardless of method used. In IE9, JAWS works the same for all methods, but I wouldn't exactly call it perfect. Whenever a same-page link is selected, JAWS announces the content at the target, but before it does so it announces the page summary data (number of headings, number of links, etc.) as if the page is being reloaded.

NVDA results are similar to JAWS. It works perfectly in Firefox for all methods. However, in IE it has one minor limitation: It doesn't support named anchors unless the target has tabindex="0". It does support id attributes as targets, with or without tabindex. This is only a limitation in IE, not in Firefox.

On Mac OS X, VoiceOver works perfectly, regardless of method.

In summary, either a named anchor or an id will work as a target in Firefox and IE, but the target also needs tabindex="0" or IE doesn't give it focus. And nothing you do with HTML alone will work for keyboard users in Chrome, Safari, or Opera.

JQuery Solution

With some simple JQuery, you can force browsers to give focus to the targets of skip nav links, plus highlight the target section temporarily so sighted users know they've arrived. This solution also improves the experience for JAWS users in IE - when JAWS users click the skip link, JAWS announces the target content without repeating the page summary (tested with JAWS 13 in IE9). You can see this in action on my Skip Navigation with JQuery page. It's also implemented here on the page you're now reading.

Here's how to implement this solution in four easy steps:

Step 1. Add JQuery Libraries

 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
 </script>

That's all that's required to make focus work, but if you want to visually highlight the target (recommended), you'll also need to load the JQuery UI "Highlight Effect". If you're using no other JQuery on your site, you can build a custom JQuery download at jqueryui.com/download that includes only the parts you need (Effects Core and Effects "Highlight").

Step 2. Add Custom JQuery function

Just copy and paste:

<script>
  $(document).ready(function() {
    // add a click handler to all links 
    // that point to same-page targets (href="#...")		
    $("a[href^='#']").click(function() {
      // get the href attribute of the internal link
      // then strip the first character off it (#)
      // leaving the corresponding id attribute
      $("#"+$(this).attr("href").slice(1)+"")
        // give that id focus (for browsers that didn't already do so)
        .focus()
        // add a highlight effect to that id (comment out if not using)			
        .effect("highlight", {}, 3000);
    });
  });
</script>

Step 3. Add skip navigation link

Add a same-page link to the top of your body. Note that users are more likely to understand, and therefore use, links with text that describes the target, rather than what's being skipped. In other words, say "Skip to Main Content", not "Skip Navigation". This is supported by research, though I can't find the citation at the moment.

 
 <a href="#main">Skip to main content</a>

Step 4. Add link target

The target should be a div with an id attribute and tabindex="-1", like so:

  <div role="main" id="main" tabindex="-1">
    <h1>Main Content</h1>
    <!-- main content of the page -->
  </div>

See the comment from Jared Smith below for an explanation of why tabindex="-1" is preferable over tabindex="0".

Thanks to Greg Kraus for helping with the JQuery solution!

21 comments on “Back to Basics: Skip to Main Content Links

  1. Excellent post! We've been using scripting very similar to this on our site for some time - http://webaim.org/media/scripts/main.js

    A few thoughts:

    - You should be using tabindex="-1" rather than "0". A value of 0 causes the element to also be in the default navigation order. It can be confusing if a non-actionable div is tabbed to when navigating the page. -1 allows it to be focusable, but only via scripting or links to the id.

    - We add the tabindex value with scripting. This means it won't work in IE if JavaScript is disabled, but we have MANY table of contents in-page links, so this is a lot easier and ensures it always works.

    - We limit the focus() event to only Webkit and and Opera, which are the only ones that need it, I think.

    - Because of the highlighting, we have CSS styles of outline:0 on the divs to remove the ugly focus indicator when the user focuses or clicks on the elements.

    - Our script will not only highlight and set focus when the user clicks a link in the page, but also if the user comes directly to a URL with a hash/anchor.

  2. Excellent feedback @Jared. I updated the post to use tabindex="-1" in the recommended HTML for the Jquery solution. Much appreciated!

  3. Yes... literally back to basics! I am interested to know about Skip to main content links for Mobile web?

    Cheers!
    Pooja Nahata
    Accessibility Lead - Cognizant

    • Good question @Pooja. I only have an iPhone to test with, but glancing just now at my test pages, all the visible skip nav links work as expected when I tap on them, and the JQuery highlight effect is visible. The links all work great with VoiceOver too. Of course, the invisible skip links remain invisible since there's no way to give them focus without a keyboard, That may be yet another reason to make your skip links visible.

      However, the real question is: Are skip links necessary for the mobile user? On touch-screen devices, sighted non-mousers don't have to tab through dozens of links to get to a particular section of the page (e.g., the main content) - they can just double-tap on that content to zoom in on it (or use whatever gesture works for them on their device). And VoiceOver users have many ways to navigate (e.g., by heading) using the rotor.

      I'm personally in favor of creating streamlined designs specifically for mobile users, then redirecting users to these pages based on screen size or user agent. Keep the design simple and there's little need for a skip nav link. Then again, I haven't done that with my blog template yet, but it's on my to-do list.

  4. Or you could, you know, include content first. Then you wouldn't need to "skip nav".

    People come to websites to consume content - not to "navigate around".

    You would then use landmark roles to define that you're starting with "main" content, and wrap the actual navigation in so that one can jump to it.

    • @Dominykas I agree that placing content first can be a viable solution, but only if done well. If the navigation *looks like* it should appear first in the tab order, messing with that convention (e.g., using tabindex or CSS) can be confusing for keyboard users. If their first press of the tab key bypasses navigation and lands them in the main content, this can be hugely helpful, but only if it's visually obvious where they've tabbed to.

  5. Good work Terrill!

    In our Drupal implementation we have changed "skip nav" to "skip to content" so it will be present on all pages. We will look at the method you describe here as a model for that implementation. Thanks!

    BTW, in Safari to enable the tab key to highlight each area of a webpage go to Preferences > Advanced and select it. Option-tab highlights only text fields.

  6. This article rocks! I've always wondered about these nuances and now I know. And super first comment from Jared. I've already made the fix on EasyChirp.com. Cheers to another great article by Terrill!

  7. Great article, thanks!
    I've got a tip to add and an issue that might be a relevant consideration.
    The default behavior in IE is as you describe.
    The behavior similar to that of Firefox can be turned on in IE temporarily by using "View" > "Caret Browsing" or by tapping "F7".
    To make this setting global, go to: "Tools" > "Internet Options" > "Advanced" and select "Enable Caret Browsing for new windows and tabs".
    We use this setting for all of our screen reader any keyboard only users.
    A limitation of this is that once you follow the Skip to main content link, using "Alt+Left arrow" takes visual focus back to the Skip link but not programmatic focus.
    The next Tab event takes you to the first focus element after the Skip link's target.
    This more problematic when navigating to and from a Table of Content section.
    This behavior is the same in Firefox and IE.

  8. Pingback: Why your ‘Skip to Content’ link might not work « WhatIsDamon

  9. Fantastic article. Provides a wonderful summary of the issue and the most complete workarounds I've seen. I found that I needed tabindex -1 in Chrome as well as IE, is that normal?

    I feel like the behaiviour of webkit is really not generally good for the web.. check my post where I provide a few links to bug reports you can weigh in on/vote up/etc ( http://whatisdamon.com/blog/?p=247 )

  10. Hello again, I've noticed that your jQuery doesn't work when there's a link with href="#". I've modified it as follows to fix it:

    $("a[href^='#']").not("a[href='#']").click(function() {
    $("#"+$(this).attr("href").slice(1)+"").focus();
    });

    • Good catch, Damon! Just to clarify - the purpose of the JQuery solution discussed in this blog post is to give focus to (and optionally, to highlight) the target of the link. Since there's no specific content targeted with href="#", your revised JQuery code applies the solution only to same-page targets other than href="#". Nice - thanks!

  11. I'm scratching my head a bit over the jQuery/Webkit thing. Your article suggests that just including jQuery is enough to make Webkit browsers move focus, but I can't get it to work. I don't understand JS, which probably doesn't help, but have I read that incorrectly?

  12. Pingback: An Accessibility Makeover: Bay Area Association of Disabled Sailors – shannon sans-serif

  13. We have found that webkit browsers require Javascript to force the focus to the right place for keyboard-only users.

    Skip to Content
    ...
    Headlines

    Otherwise the keyboard focus stays on the skip link. This can easily be verified on your test page.

  14. Pingback: Getting Down and Dirty with Accessibility and Usability – #TCUK12 Workshop - Mardahl.dk

  15. Pingback: Suggestions for the new Disability.gov | Web Axe

  16. Pingback: Podcast #95: Global Accessibility Awareness Day, Surveys, more | Web Axe