Why your ‘Skip to Content’ link might not work

Did you know that a skip link in Chrome or Safari cannot work properly without using JavaScript?

I stumbled onto this factoid today and I was pretty stunned. I’d just assumed all the techniques for implementing skip-links (posted online and in various books) would work as written.

But it turns out that if yours aren’t working, it might not really be anything you did wrong (though it sort of is, because as a web developer you probably realize you are responsible for knowing exactly how every rule and behaviour works in every browser still being used). The real culprit is Webkit. (webkit is the engine that Chrome and Safari are both built on).

Here’s a bug report about the issue that was submitted 4 years ago. Nothing has been done to address it.

What’s most concerning is that this doesn’t seem to be common knowledge in the web design community and most solutions for skip-links posted online don’t realize this particular limitation of webkit browsers. As a result, thousands of pages that were built to be accessible are very likely not functioning properly for 33% of users (Safari and Chrome’s combined browser share, and that’s not even including mobile traffic). That’s a big problem.  Let me explain a bit about accessibility, skip-links, this particular problem, and what you can do to help.


Go here and star/favourite the issue so it hopefully gets fixed


Accessibility is a very important (and sadly too often neglected) part of making any website. Just like businesses need proper wheel-chair ramps and handrails, any website needs to take into consideration users who access the web in non-traditional ways. This can be with a screen-reader, keyboard-only navigation, or a variety of other assistive devices.

The W3C’s Web Content Accessibility Guidelines outline several steps site-creators should be taking to ensure sites are usable by everyone on the net.  They’re divided into 3 levels of increasing complexity and accessibility, and by the time we get to level AAA, I can really understand not following everything to the letter, but I think as a community we should at least stick to Level A.

One of the Level I requirements is:

2.4.1 Bypass Blocks: A mechanism is available to bypass blocks of content that are repeated on multiple Web pages. (Level A)“. (source)

This behaviour is facilitated by a technique known as a “Skip to Main Content Link” or “Skip Link” and has been non-standard practice for at least 8 years.

There’s a related mandate: “2.1.1 Keyboard: All functionality of the content is operable through a keyboard interface” which is not exactly negated by this bug, but it is certainly made far more annoying for the user.

Skip Links

A skip link is an inline link that brings you to a point on the page right where the main content starts. Most pages start with all sorts of navigation and other data that doesn’t change from page to page. Just imagine if someone was reading the code of a page to you line by line how annoying it would be to sit through all of the main menus, sub menus, header information etc on every page of a site you visited!

A lot of people don’t want this visible on their site so they find creative ways to hide it. People used to apply the visibility: hidden; or display: none; CSS rules to the links, but this isn’t reliable because many screen readers read that rule and ignore the content as well.  These days skip-links are usually hidden by positioning them outside of the page with overlflow: hidden; or by using clip: rect(1px 1px 1px 1px);.

However you do it, though, it’s not going to work in any webkit browser without having to code up some JavaScript to mimic the functionality that ought to be built in.

The Webkit Problem

It’s a bit tricky to explain. So just try this.

  1. Open Safari or Chrome
  2. Go to Drupal’s homepage. (ooh! blue!)
  3. Press Tab. (Aha! a Skip Link hath appeared)
  4. Press Enter to skip to the content (Bam! This is fine. This is dandy. I don’t see what he’s whiffling about!)
  5. Well, let’s say you want to look at the Pagebuild Case Study link. Great! Why don’t you just tab over to it, then.
  6. You see the problem.

Because the highlight stays on the original link when going to any internal link, as soon as you start using the keyboard you get thrown right back to where you started!

And this doesn’t just affect Skip to content links, it makes any internal link if not useless then extremely frustrating with keyboard navigation.

It’s not a ridiculous way for things to behave if you think about it from one angle. But if you’re thinking about it from this angle, it’s pretty bad, wouldn’t you say?

So What Do I Do?

[Edited to add:] Martijn van der Ven has written a complete solution that does not require jQuery and Paul Duffy has one as well. They’re new so please have a look and test them out; I am not sure the benefits or drawbacks of either method, either.  Both provide a much better universal solution than a line of jQuery.

For now, an article  by Terril Thompson has some great explanations and a workaround that uses JavaScript to make a fully-working-in-every-browser skip-link solution. Fwiebsls! An alternate version  is also available, but I’d recommend the first as it handles inline links generally, not just skip-links (though it does require jQuery).

[NB: I've slightly modified Terril's jQuery as outlined below because it had issues with href='#' anchors]

// Apply focus properly when accessing internal links with keyboard in WebKit browsers.
$("a[href^='#']").not("a[href]='#'").click(function() {

So great! There’s a workaround. But that only helps the person that happens to be on the site that you designed using the workaround. That’s probably not most people who need it to work. It also doesn’t help the broader problem of wonky internal links.

What really needs to be changed is this default browser behaviour. The easier it is for a site to be accessible out of the box without any arcane bug/hack/workaround knowledge requirement, the more accessible the web will be in general. And that’s a good thing.

Today in my searches I found the bug report I mentioned up-top. I don’t see a way to encourage it to be solved, though. There’s a similar bug report on the Chromium issue tracker (Chromium is the open source framework that Google Chrome is based on).  If everyone I know and everyone you know goes to that page and clicks the star to ‘favourite’ the issue, maybe more people will take notice. So please do that! And if you are into web standards and accessibility, share that link around!

Please let me know your thoughts on this, or if you have ideas of how to get this addressed quicker, or worked around more elegantly or if you don’t think it’s important at all.

(And don’t you worry the irony of me posting this on a site without a skip-link is not lost on me)

Huge thanks to Aaron Gustafson and .Net Magazine for spreading the word on this!

Tags: , , , ,

13 Responses to “Why your ‘Skip to Content’ link might not work”

  1. tyler says:

    Absolutely it is important, do not hear me wrong here, but it is my guess that if something so important is broken on so many sites you are not going to see a very big usage by the % of the population that does need those. Absolutely correct me if I’m wrong (because this is just here say i’m repeating) but the best screen readers out there are still PC-based (so webkit will have a smaller footprint) and I assume due to the slow-moving process that is accessibility software that older versions of IE are prevalent among those needing those tools.

    Again, absolutely should be fixed in webkit, just wondering aloud about the current impact

  2. Damon says:

    I see what you’re saying, but I think the numbers game doesn’t really apply to accessibility issues. I should have focused more on the point that ‘skip-links’ are just the tip of the iceberg, because in fact *every internal link targeting the same page* cannot be properly navigated by keyboard in webkit. I’d consider that a pretty big deal–although you’re likely right that it’s only affecting a tiny percentage of users

  3. Thank you for opening my eyes! Immediately I starred/commented the bug on Chromium issue tracker and on WebKit bug tracking system!

    I also tried to use the Martijn van der Vens JS only fix in my current project. The problem is the fix only works inside my dev environment (Coda) but outside using Safari 5.1.3 the focus is not set to the anchor target.
    Any ideas why?

    • Damon says:

      I’m not sure! I haven’t yet had a chance to use Martijn’s fix. Maybe post an issue on the gist and see if he can help figure out the problem. It would be great to have a solid, universal workaround to plug into all websites.

  4. OK – I found what causes the problem (it’s not the Safari version):

    In my dev environment my templates don’t have a base tag inside the header. But on the life site the base tag is required by the CMS (MODx Revo)!

    If I remove the base tag – the Java Script works as expected and the anchor target gets focus.

    So the script of Martijn definitely has a problem with base tags.

    I couldn’t post an issue on Git because the code is hosted in a “Gist” and there you can’t pos issues.
    I’ll try to contact the author via Email.

  5. Paul Duffy says:

    OK, so Chrome and Safari use CTRL+OPT for shortcut keys. Guess what also uses CTRL+OPT for shortcut keys and takes precedence over all applications? I’ll give you a clue: CTRL+OPT+S or VO-S as they like to put it is mute/unmute.

    Start reading from chapter one:

    Spend half a day testing my own solution, get on a Mac and it’s failyfailyfailhorse all the way.

    I will see if anything can be remapped when I’ve STOPPED SCREAMING!!!

  6. Paul Duffy says:

    Safari’s doesn’t like my code either.
    Works fine in Chrome.

  7. Paul Duffy says:

    For the following I’ll follow the Voiceover convention of referring to CTRL+OPT+ as VO-

    OK, last word. Seems that sometimes VoiceOver will autoread the page only if you hit VO-A
    In Chrome hit VO-TAB then VO-accesskey
    In Safari the accesskey combo key reverts to CTRL when voiceover is running.

    In both cases the focus will arrive at the first readable item, after which I seem to need to hit VO-A to read the first item then use VO-left/right Arrow for each paragraph element.

    I don’t know if this is expected behaviour or which key would have it continue in a smooth flow. Could be worse, Opera on Windows doesn’t seem to communicate with NVDA at all.

    This is all testing with my own code at if anyone has any input.

  8. Martijn says:

    Paul, nice work on the script.

    I’d like to ask you why you opted for addEventListener instead of onclick? The latter makes for less bytes. Another possible problem with addEventListener is that it binds our focus changing routine to links that might already have other click events set, possibly messing with what the developer intended to happen.

    I hope you wont mind if I lend some of the additions you have made for a future update of my script (

    Also a small tip: you have linkList[i].hash==linkList[i].href in your code. This is always going to be FALSE, unless the href is an empty string (in which case it would be “”==””, pointless). This is because the href property of a link element is not the same as the HREF attribute. The href property is always an absolute link and the hash property is always the fragment identifier. So you can strike that comparison.

  9. Martijn says:

    Ignore my addEventListener rant, I have since switched to it. It’s simply better.

  10. Paul Duffy says:

    Hi Martijn,

    Quick note on addEventListener, the third option (useCapture) is only optional in newer browsers (FF6 and upwards for example) and should be set to false. I’ve got a little too used to jQuery handling these for me xoP

    You’re right about the href, I think I got stuck in IE6 compatibility mode. Also, location.href should probably be switched back to document.documentURI to handle iFrames instead of always referring to the address bar. In this event I’ll be changing the second line to

    if (!!document.documentURI) {

    IE9 still only supports document.URL and has no such problem with the accesskeys but it is much more likely to bail out and stop all JS if it finds something it doesn’t like.

  11. Much traffic on this bug now!
    (maybe a fix is near for WebKit)

  12. Damon says:

    Oooh! Awesome to see something happening on this!

Leave a Reply