Slick: Positioning issue when tabbing through items with links

Created on 16 Nov 2014  路  35Comments  路  Source: kenwheeler/slick

When slider items contain links and when you tab through them the positioning of items gets all screwed up. Depending on browsers the position gets recovered or not screwed at all.

JSFiddle: http://jsfiddle.net/tu2xqtej/4/

Safari 8: Nothing gets screwed because the tabindex never reaches links within slider items. Not very accessible
Chrome 38: position gets screwed and does not recover when sliding again (with arrow or arrow keys)
Firefox 33: position get screwed but recovers when sliding again (with arrow or arrow keys)
IE11, IE10 and IE9: Behave the exact same as Chrome does

I'm sure the tabindex behaviour is not something you can really do anything about since it's browser behaviour.

I think some other slider libraries don't have this problem because they are positioning items with CSS "position" either relative or absolute where as Slick is using floats to position items next to each other.

How to solve this?
Using CSS "position" might come to the rescue. However, if you tab through the items the link will be selected but you will see nothing and it would appear if the 'tab' button isn't doing anything. At that point it's cycling through all the "invisible" positioned items. Not very accessible.

I think a better approach would be to see if you can listen (with JS) to the tabindex and have the slider animate when the focus is on a tab in a slide. However this would also mean that if you have three sliding items and there's only a link in the first and last item it would skip the rest and go to the last item immediately. This is also what happens in the second slider in my JSFiddle example. To fix that you would have to add a "tabindex" to every slide so you could easily always slide through each item. That's what I did in the third slider. However you would always have to go through each slide before you could continue when just using the keyboard. and this method is also not working in Safari.

Not sure if solvable and what the best solution would be. But thought I'd at least let you know that I ran into this problem and some ways I thought of solving this.

Investigating

All 35 comments

There is literally nothing I can do about this. It's devastating. See here: http://codepen.io/kenwheeler/pen/AlmcI

I'll take a look and see if I can force the slide to take the tab first, and then slide it in on focus

Man that's a bummer. How about looking for the current focused element and check if the next focusable element is within a slick slider? Then on keyup you could slide to that particular slide. Although you will probably have a problem finding out wether the keyup will go to the next or previous tabindex.

The tabindex for each slide might be the better solution. You could either make it a setting to enable/disable that functionality or only add the tabindex to slides when a link within slides is found. That way if you have a simple carousel/slider with just images you don't have to tab through the whole slider.

As an extra check I think it would also be wise to include some logic in the plugin to check if the slider has been re-positioned without using 'slick' functionality and the set the current slide index to the actually shown slide. This is not happening in my third slider example on the JSFiddle resulting in weird slider behaviour when sliding after tabbing to a next item. (for example tab to the third slide and then slide with the arrows, you will get empty slides at the end) This check could also only be done when a "tabindex" element has been found within the slider for performance reasons.

I don't have any suggestions at the moment, but this topic seems very related/relevant to https://github.com/kenwheeler/slick/issues/716.

This is indeed very related/relevant to #716

I've been thinking and tabbing through the carousel (tabindex on every slide) also explained in #716 feels like the best and robust solution. You have to make sure is that the slider is being activated (update the current slide-index without fade or slide effects (since slide is already in view) to make sure the tabindex and slider are in sync). Also going through the tabindex reversed is slightly problematic since the link within the slide will come before the slide's tabindex.and it will screw up the slider position (slider position will be fixed when reverse tabbing one more item to the actual slide)

The downside about this technique is that a keyboard user always has to tab through every item which could get a bit annoying when there are many slides.

I thought of a solution and created a JSFiddle for that:
http://jsfiddle.net/f9wd8thj/1/

Basically a "skip link" appears (only on :focus, built with CSS) to skip the whole slider and go to:
a: a "skip to the start of slider" link that also appears on :focus. Press enter to see effect.
b: skip to the next focusable element after the slider (not built in JSFiddle just an idea)

When going through tabindex in reverse you could:
a: show a "skip to the start of slider" link on focus which brings you to start of slider. Press enter to see effect.
b: automatically bring it to the start of the slider (however this is not something one might expect), Aslo not built in JSFiddle, just an idea.

That still leaves us with the reversed tabindexing within the slider itself when links are present. Might be a tough nut to crack. Also you're going to need two more options for the labels on the buttons to make sure you can use the plugin in your own language..

Now I'd like to help out but my JS skills aren't good enough to build the actual functionality. Hope my ideas are of some help. If you don't want to hear them anymore please say so and I will stop trying to pursue perfection ;)

@robinpoort That's clever--I like the skip links that appear for tab users. That seems like a good accessibility function.

But yeah, this stuff does get really tricky, especially given that you can mix the use of arrow keys as well as the tab key. I'll have to chew on this further.

Happy Thanks Giving... Added the ScreenReader & Keyboard Accessibility

http://jsfiddle.net/equinoxs/u5j2wcjs/1/

Please let me know your thoughts. I will create a pull request.

Thanks

Awesome! Great work :) Smart to use and set tabindex to -1. That way you never have to scroll through the whole slider either.

I've tested this on Firefox and Chrome now and stumbled upon the following bug:
When you are focused on a single slide (

) and then start using the arrow keys the slider just works and goes to next/prev slide. However if you press tab to go the next slide then sometimes the whole slider doubles in height and adding.

double1
double2

Ok scrap that, that's because of the border used on :focus which messes up the flow. Reason I'm not deleting this is so other testers know it's actually not a bug. Here's an example that uses box-shadow instead to keep out of the flow: http://jsfiddle.net/u5j2wcjs/3/

A note: since tabbing won't take you to the next slide it might not be necessary to add a tabindex to each slick-slide? Just have a tabindex on actual links inside each slide? That way we don't have the 'double focus' on the start. Right now you focus on the slider first (to navigate with arrows) and then you go the the first slide which at this moment (full slider) looks identical. Although that would be different with a carousel. Might not be needed to focus on single slides though and remove the tabindex on slick-slide altogether.

Anyways food for thought. Great work!

Remember: box-shadow would be considered un-accessible for visually-impaired users since older browsers wouldn't be able to render it. I just tried to replace it with outline in that jsfiddle @robinpoort shared, but the outline doesn't get rendered on the main slide item (outline sits outside the box-model). but I think you can fix that by adding 2px padding to the slider track.

I would consider adding:

:focus {
    outline: 2px solid #ff9900!important;
}
.slick-track {
    padding: 2px!important;        
}

instead of your box-shadow declaration, to make it properly accessible :)

http://jsfiddle.net/1x8w9c3n/

The focus style is added to make sure the user the sees the current focussed element.
We can use the 1px dotted outline. This will make sure we have the web standard of focussed element.

@robinpoort:
Yes, we need to update the focus only for the slider list. (May be the first a || input || button || Current List )

The current HTML structure in the slick is little bit un-common for the tab-index to follow. The following would be perfect for ADA compliance.

  • Previous Button
  • Slide
  • Next Button
  • Then the controller.

Also, we may need to update the keyHandler so that it works anywhere inside the slick-container instead of just the list. This will make the slick more keyboard accessible.

Just my thoughts...

Thanks

I have made some minor tweaks to make it more accessible as per earlier note.

http://jsfiddle.net/equinoxs/1x8w9c3n/1/

  1. KeyHandler - should be able to use keyboard arrows if the focus is anywhere inside the slider
    _.$list.on('keydown.slick', _.keyHandler); //OLD
    _.$slider.on('keydown.slick', _.keyHandler); //NEW
  1. To avoid the above inside the form fields.

           if(!event.target.tagName.match('TEXTAREA|INPUT|SELECT')) {  // NEW
    
             if (event.keyCode === 37 && _.options.accessibility === true) {
                 _.changeSlide({
                     data: {
                         message: 'previous'
                     }
                 });
             } else if (event.keyCode === 39 && _.options.accessibility === true) {
                 _.changeSlide({
                     data: {
                         message: 'next'
                     }
                 });
             }
         }
    
  2. Removed the focus from the slider wrapper

         // if (_.options.accessibility === true) {
         //     _.$list.prop('tabIndex', 0);
         // }
    
  3. Added the focus for the first focusable element inside the slide.
  4. Updated the focus style.

Please review and let me know your thoughts.

Thanks

Sorry for replying this late. Been very busy and away!

Anyways awesome work again :)

  1. Works like a charm
  2. Very good now the arrows are working inside each slide
  3. Not convinced about this one. Maybe we should put the focus to the slide after all? This functionality is especially weird when using the arrow buttons to navigate since the focus jumps from the arrow the the first focusable element. I would probably go for focusing on the slide itself and let the user take it from there. Thoughts?
  4. Yup box-shadow is unaccessible. That box-shadow was only for demonstration purposes since border broke the script and outlines weren't visible. Slick shouldn't come with a styled :focus class. This is something that should be done by the website's CSS file :) And if the website's CSS file doesn't apply styling it will automatically fall back to the browser default so nothing we have to worry about.

I will do some more extended browser testing once we are all happy and ready with this :-)

Regarding (3):
I agree with @robinpoort that it seems problematic to have the focus jump to the first focusable element when you're using the arrow buttons. That seems like a no-no from an accessibility standpoint (ie, it is potentially disorienting to have the focus jump like that), and it makes it impossible to easily cycle through slides using the arrow buttons via the enter/space key (which those with vision difficulties and/or using screen readers might do) because the focus immediately leaves the arrow key.

That aside, I could go either way, but I do lean a little more towards _not_ having the first focusable element automatically receive focus, and instead let the user tab once to put focus on that element. And that has the additional benefit of resolving the above problem as well.

One other thought: It seems like currently the focus moves from within a current slide to the prev button and then to the next button. I'm a little ambivalent about that and am wondering if it would be better for the order to be prev - slide contents - next? In some ways that seems more intuitive to me (and makes visual sense).

But one thing that just occurred to me regarding the arrow keys and the automatic element focus discussion is that if a user is navigating via the next button they then have to shift-tab to cycle backwards through the links in the slide. I'm not sure if that is the way to do it or not. This accessibility stuff is hard to get right (it seems).

If you all haven't already it'd be worth it to review the ARIA recommendations for tabpanel, as that seems to be the closest analog for sliders (per https://github.com/kenwheeler/slick/issues/716#issuecomment-62072030).

Overall, this is looking great though! Good work! :)

@robinpoort @msrafi Do you guys have this in a branch?

@kenwheeler That would be great.

@robinpoort @EnigmaSolved I agree regarding #3. From the UI aspects it may look great. But for any major content driven or enterprise level applications, only the anchor and form-elements are considered as focus-able elements. Any good solution?

@EnigmaSolved, You got the right point. As I mentioned earlier in my note, we should follow the below markup for smoother tab flow.
Previous Button
Slide
Next Button
Then the controller.

Thanks

@msrafi @EnigmaSolved I like the proposed markup so tabbing through will get you to the Prev button first. If we need some additional styling for that then that is something I actually can work on ;)

As for tabindex on slides. I think with these changes (using the negative tabindex for other slides) we can just remove the tabindex on the active slide. Having focus on elements you can't do anything with is quite useless and only creates the need to press the tab key an extra time ;-)

This would give us the following:

<prev button tabindex>
<slider tabindex> to make sure we can also use the left and right arrow keys on the slider
<slide><a tabindex> (or any other focusable element) If available, remember we can also have sliders / carousels with images only
<next button tabindex>
<controller><control tabindex>

Then the automatic focus on slides/focusable elements. I think it would be best to get rid of this? Then there's only the problem of getting to focusable elements within that 'new activated' slide. The solution would either to do nothing at all and just let the user tab (coming from prev button) or shift+tab (coming from next button or controller) back to the slide or we could use a key combination like up arrow only or ctrl+up arrow to set focus to the first focusable element within the current active slide (if focusable elements are available).

Thoughts?

Doing some testing with screen readers...

@msrafi def let me know the results

Did some research with screen-readers. I removed the focus from the first element and added the focus to the current active slide. The screen reader announces the details after making some minor changes.

http://jsfiddle.net/1x8w9c3n/12/

Made following changes in this version:

  1. script updated for markup changes to make the focus goes through a flow. PrevButton - Slider&Contents - NextBtn - Controls.
  2. Updated aria-labeledlby to aria-describedby for sliders and its respective controls.
  3. Focus moved to current active slide.
    4.Btw, left and right arrow key should work as long the focus is anywhere inside the whole slide.

Let me know your comments.

Cool stuff!

  1. Looking good, working super nice
  2. Awesome
  3. I would still don't touch the focus when using the prev, next and controller buttons. I could imagine it is super annoying that the focus jump to the active slide when you are using the arrows on controls. I get why this could also be a good option considering you point 4 that you can use the left + arrow keyboard arrows to navigate but I still think it would be better to only set the focus to the active slide when you are actually inside the "slide" area. Not sure if I'm making any sense here :) Basically what I'm trying to say is never touch/move the :focus when using the previous arrow, next arrow on controller. Only touch/move the :focus when you are already in the slider area, makes sense?
  4. That is really great!

Makes sense?

@robinpoort I understand the focus jumping will confuse the users. Thank you for pointing it out.
Here is another solution.

http://jsfiddle.net/1x8w9c3n/19/

The focus will not change for slider event handlers (Arrows & controllers). But if the focus is inside the slide child elements, then the focus will moved to current slider after the animation is done. Hope it make sense.

Thanks

Hey @msrafi

Totally makes sense and this is both looking and working awesome now!

I think I am done here for now.

Awesome I'll get this stuff in core after the holidays. Thanks @msrafi

Always welcome... Have a good holidays

Awesome work @msrafi!

Looking forward seeing this in the core :)

Happy holidays people!

@msrafi @kenwheeler One more note on the prev button, next button, slider and controller. Although this is working perfectly now it only allows us to style the elements a certain way.

Take the following examples:
sliders

The slick in this branch has a good accessible keyboard focus flow for 'Slider 1'. However the current stable slick way of doing things would be something like 'Slider 3' but then with the controller last and buttons first.

Maybe this is a discussion for another time and place but it might be wise to consider the following:

  1. Add a setting where you can define the order of the elements e.g. order: ['prev', 'slider', 'next', 'controller'] or order: ['prev', 'next', 'slider', 'controller'] etc.
  2. Let the user add a holder/container to the markup instead of JS handling it. That makes for perfect customization. Either with pre-defined classnames or as a setting telling slick which container is what (but fewer settings is always better). 'prevbutton: '.slick-prev'` etc.

Not sure which of them would be better. Adding markup with JS make markup files harder to read though so adding it manually would be a good practice. But then it makes it harder fo more people to actually use slick.

Food for thought :)

@robinpoort Thats a nice concept you have here. I think we can do that. I will try something later this weekend.

@msrafi That would be cool :-)

Guys, I spent some time since last weekend and found the changes that I am making is affecting the performance when using it with breakpoints/resize. I would consider this would be a good to have feature with design dependency. But, we can close the accessibility issue and open it as a new feature. Will try later when I get more time.

Agreed let's handle this in a different issue:
https://github.com/kenwheeler/slick/issues/912

@kenwheeler Any chance the tabindex change is merged soon, so using tab-index doesn't destroy the slider anymore? Willing to help if needed.

This seems to be the relevant PR: #1146

Has this been addressed yet? I'm having the same issue as the original poster and it's almost 2017 here.. =\

I have a work-around that seems to work pretty well, but it feels hackish

http://pastebin.com/3ybaRSjQ

camdagr8 - I don't think this has actually been fixed totally, at least for the version of Chrome that runs FireTV web apps - gonna look at your code tomorrow.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

xitongzou picture xitongzou  路  3Comments

jamesinealing picture jamesinealing  路  3Comments

k-lusine picture k-lusine  路  3Comments

REPTILEHAUS picture REPTILEHAUS  路  3Comments

crstauf picture crstauf  路  3Comments