How to Accessibly Rotate Contents with jQuery

Together with the parallax scrolling effect, rotating content is another example of an effect that you often see implemented in websites. You can see it used to rotate news, tweets, Facebook posts, and so on. When built using jQuery, often developers create the widget employing the hide() and the show() methods (or the similar methods such as fadeIn()/fadeOut() and slideUp()/slideDown()). The problem with them is that, after performing the animation (if any), these methods change the value of the display property of the selected element(s) to none and back to the original value respectively. This behavior leads to an accessibility issue.

In this article we’ll describe what the issue is and how you can use different jQuery’s methods to achieve the same effect but taking care of accessibility.

The Problem

Some people, usually but not limited to visually impaired people, use the TAB key to navigate a website. If you’re not familiar with this concept, what happens is that every time a user hit the TAB key a focusable element of the page is focused. The order in which the elements are focused follows the the order they appear in the DOM (exceptions apply) starting from the very beginning of the page. Some examples of focusable elements (learn more in When Do Elements Take the Focus?) are links, input fields, buttons, and everything that has a value for the tabindex attribute greater than or equal to 0 (learn when and how to use tabindex). The important point to understand is that an element hidden by setting its display property to none is not focusable.

With this in mind you can see that if a TAB user is focusing an element (for example a link) that has been hidden using the hide() method and then the user presses the TAB key, there is a problem. Being hidden in that way is like the element has been temporarily removed from the DOM, so there isn’t a next element to focus. What browsers do in this case is to reset the position, usually focusing the URL of the page. This causes a lot of frustration to the users because they have to start from the beginning of the page every time they reach this death zone. Even more, some of your users can’t even see your awesome rotating effect – all they want to do is access your content easily.

Show Me the Code

To give you a better handle on this situation, consider the following markup:

<div class="rotating-content">
   <p>This is a text with a <a href="#">link 1</a> and <a href="#">another link 1</a></p>
   <p>This is a text with a <a href="#">link 2</a> and <a href="#">another link 2</a></p>
   <p>This is a text with a <a href="#">link 3</a> and <a href="#">another link 3</a></p>
   <p>This is a text with a <a href="#">link 4</a> and <a href="#">another link 4</a></p>

To rotate the content of the div you might write code like this:

$elements = $('.rotating-content').find('p');

setInterval(function() {
   $elements.filter(':visible').fadeOut('slow', function() {
      $next = $(this).next();
      if ($next.length === 0) {
         $next = $elements.first(); 
}, 4000);

Putting everything into action results in the following demo:

Accessible Rotating Content

To solve this accessibility issue while achieving the same effect, we need to employ a different jQuery method, fadeTo(), and one of my favorite CSS helper classes ever, usually called visuallyhidden or visually-hidden. The code of this helper class is shown below:

   border: 0;
   clip: rect(0 0 0 0);
   height: 1px;
   margin: -1px;
   overflow: hidden;
   padding: 0;
   position: absolute;
   width: 1px;

Adding this class to any element will visually hide the element without setting its display property to none.

Now, instead of using the hide() method to hide the element or the show() method to show it, we’ll add or remove the visually-hidden class as needed. Besides, to recreate the nice animation we’ll use the fadeTo() method. The latter allows you to set the opacity you want the element to reach but when it has completed its task, it won’t update the display property (to learn more about this method you can refer to the official documentation). So, we’ll pass to it the value of 0 when we want to hide the element and 1 when we want to show it. Finally, we’ll also set the initial opacity value to 0 to all the elements we want to animate.

The resulting code is shown below:

$elements = $('.rotating-content').find('p');
   .css('opacity', 0);

setInterval(function() {
   $elements.filter(':not(.visually-hidden)').fadeTo('slow', 0, function() {
      $next = $(this).addClass('visually-hidden').next();
      if ($next.length === 0) {
         $next = $elements.first(); 
      $next.removeClass('visually-hidden').fadeTo('slow', 1);
}, 4000);

Putting everything into action results in the following demo:

With this simple change in the code we’ve created a more accessible widget to show rotating content. To see the difference between the two demos I invite you to use the TAB key to navigate the page.


In this article I’ve described an important accessibility issue that can be found in some code used to create a rotating content effect. Some of you may have unintentionally added it to a website built in a widget like the one discussed, but there is nothing to be ashamed of. Everyone ignores a lot of important notions before they learn about them. Yours truly discovered this important issue not so long ago (thanks to the work of people like Steve Faulkner and Léonie Watson).

While learning this simple trick may help you, the main take away lesson here is that when you develop any feature of a website, you should also verify that is accessible, otherwise you risk locking some of your users in an inferno of frustration and inaccessible content.


  1. “Rotate through”, perhaps?

  2. You’ve never heard of sighted users navigating with the keyboard? So you’ve never considered parkinson’s disease, or motor neurone disease, or hand tremors, or stroke-related movement problems, or any of a hundred different conditions that make it difficult or impossible for someone to use a mouse with any degree of precision?

    Sorry if that sounds sarcastic, but you’re promoting accessibility at the same time as denying of its principle audiences. It staggers me to hear that. And that you further claim – the fact that most developers ignore this issue means that the problem doesn’t exist – is simply astounding.

    I never argued against carousels, I simply pointed out that you have a long way to go before your solution is accessible. You wrote this article to encourage developers to take a more accessible approach to this particular kind of script, but you haven’t actually produced a better solution. It’s better in one way (not resetting focus), but it’s worse in another (hidden links in the tab order), and it’s just as bad in another (no control over the timing).

    You have learned much, but you are not a Jedi yet :sunny:

  3. Please stop this “you are not a Jedi yet.” thing. I never stated to be an accessibility master, not a ninja, not a Jedi. With this article I’m proposing a better solution to a problem, not a 100% bullet-proof approach. I’ve never said that this solution will make everyone in the world happy, even those with some disease I’ve never heard of. It simply improves the accessibility of a widget that is often used in websites and at least for me, doing something is better than doing nothing.

    You say:

    this is false. With the normal approach you have hidden links too that you can’t reach with the TAB key or any other key. You have to be lucky enough to focus the link you’re interested in when you press the TAB key. For example if you’re already in the widget focusing a link, you can’t go to another link and you have to start again. It’s like the skeet shooting, you have to calculate the right timing. With the solution I’ve proposed in the article the user can reach the content at any time. So, even if a user has to press the key more times as you said, he/she still can access the links which is an improvement. Maybe not the best solution in the world and that work in every case possible but still an improvement that everyone can achieve with a few lines of JavaScript.


    If you’re talking about timing, yes you are talking about carousels and their usual features of being automated and set to a given speed/timing which causes issue to many people. Once again, this is an issue that affects all the carousels in the world, at least the automated ones with no arrows to change the content. In the article I’m describing these kind of widgets. If the article were titled “How to create an accessible carousel” I’d say you’re right on this point and that I missed it. But what I’m discussing here is different. The problem of carousels isn’t timing, is that they shouldn’t exist at all in most cases.

  4. Several of us here on the forums, actually. And everyone who needs to use for example Dragon Naturally Speaking, or people like Chris, an Apple-certified Final Cut Pro video editor using switch controls and the like. You may not know people using switch controls (I used to not know any), but I can assure you they exist, and they be internettin’ with the rest of us, cleverly hidden behind standard browsers. “On the internet, nobody knows you’re a dog.”

    Keyboard means more than keyboard. Keyboard is generally the replacement for many other computer access methods, meaning when something isn’t keyboard-accessible, it’s often not accessible with other softwares either. And a lot of those users of other softwares can and do see, but this can go along with the “accessibility means blind people/screen readers/ARIA” fallacy which I myself have fallen into. The accessibility community has started to even worry a bit about that. The largest group of whatever we want to call “disabled” tends to be more motor and cognitive than blind or low-vision, and people who are losing their vision or just have very very bad vision tend to still use their vision as much as possible (noses pressed against the monitor with the computer resolution down to 600x800 and using high contrast settings, even if a screen reader or magnification software would be so much easier to use).

    I’ll give you visually impaired/dyslexic with something like ZoomText too: you can turn on the Reader function, to also hear where you’re either mousing over or where you’re tabbing, depending on which method works best for you. It’s generally a Good Thing when what you see on the screen matches what you hear and where you are. Hearing stuff you can’t see freaks out plenty of people, because users have expectations and that’s one of them.

    Lastly I’ll throw out blind colleagues who often need to be able to work with their sighted colleagues and figuratively, but I guess also literally, have to be on the same page : ) Ha ha, look, I pun. You laugh.

    …no potato, okay, moving on.

    It is, but usually for other reasons (though the article addresses one, focus going back to the top each time something deep in the page is activated… arg!): autofocus on inputs, lack of focus rings/styles, getting stuck in jQuery autocompletes and AddThis/ShareThis craptastic icons-links are actually way, way worse. Tabbing through an otherwise well-built site is totally doable, especially if you must (I am not limited to keyboard, but it’s usually the first thing I start with on sites). You should see what switch users do: they get a little control panel where the focus moves one thing or maybe chunk or row at a time, and you select the one you want with your switch when the focus gets there. Would be damn tedious to me as well, but if I did it every day, well. Would rather be internettin’ or mailin grandma or whatever. If you need keyboard, what’s your other option?

    I agree screen readers make things nice, which is why I’m still hanging a deathgrip on my old Opera12… you can move focus by headings, and form-control focus is separate from link focus. Pretty kewl. Wish this existed today as more than yet-another-slow-your-browser-down Firefox plugin.

    Just because carousels are crappy doesn’t mean anyone gets automatic exemption from Good-Devs-Try-To-Follow-WCAG (SC 2.2.2): pausing stuff that moves is a sort of requirement anyway-- no code examples should leave it out, because we all know developers copy code examples. Good or bad, it’s how it is.

    (This caused a bit of a kerfluffle recently when a Google team released a polymer Gmail demo complete with pretty box shadows and stuff. Many in the community pointed out that, especially with the name Google behind it, this demo will become the basis of many other developers’ Polymer code, despite not working with keyboard.)

    I didn’t read the article as anything other than trying to be helpful to devs. But here in the comments, I rather dislike the “you usually eat poop so eating dogfood is better right?” vibe I’m feeling (maybe it’s just me, I don’t want to put words in anyone’s mouth, especially not the word ‘poop’). Yeah, it’s better, but we want people-food. And when I’m duckduckgo-ing for “accessible carousels” as a conscientious developer, this article ought to turn up (being recent, and on SitePoint, and having a somewhat-well-known author to boot).

    This could be a part 1 of however-many articles series for “jQuery accessible carousel” where in this one, the point is how to stop focus from being super annoying. So many people are looking for how to do this, especially with jQuery-- so I’m definitely looking forward to more articles on this. They’re not easy, but then again if we as devs are forced to build a carousel anyway, then we should take it like adults and admit that yeah, we’re going to be adding a bunch of JS-crap for manual focus management and whatnot… we do this already with our accessible modal dialogs (good god), accessible dropdown menus (ug) and accessible tab-panels (lord on a stick). They suck to code. We do it anyway. And we all want to see How Should We Do It. So write more!

  5. btw – Russ Weakley and I are doing a Learnable course next year, looking at some of the most popular widgets and talking about how to do them accessibly. The three things you mentioned (modal dialogs, dropdown menus, tab panels) are all included :slight_smile:

16 more replies