Home » Jquery » javascript – Setting an element's focus only if user has tabbed to it

javascript – Setting an element's focus only if user has tabbed to it

Posted by: admin February 22, 2020 Leave a comment

Questions:

I am trying to make some collapsible accordion containers on my website accessible, but I am running into an issue.

The accordions are controlled by link elements on the page – this way, a keyboard-only user can tab to them and access them. The first issue I ran into was that if a user tabbed to one of the links, the page wouldn’t always scroll up to show them which one they had tabbed to. I fixed this issue setting the focus using the following code, which scrolls the link to the top of the viewport:

$(".accordion .accordion-item .accordion-heading a").focus(
    function()
    {
        $('html:not(:animated), body:not(:animated)').animate({
            scrollTop: $(this).offset().top
        }, 250);
    }
    );

The problem I am encountering now is that when a mouse-user clicks on the link, it jumps to the top of the page and does not open the container unless the mouse-user clicks the link again.

Is there a way I can set the focus code above to only fire if the link has been tabbed to? Or, is there a better way of handling the focus issue so that it works for both keyboard-only and mouse users?

Thanks!

How to&Answer:

Firstly a quick apology, having now seen your accordion is built correctly, links with in-page anchors are actually preferable if the accordion is constructed using javascript on page load and falls back to just a list of in page anchor links and content between them.

I am that used to seeing <a href="#"> on accordion openers and weird accordion implementations I jumped to conclusions, change it back from <buttons>!

Fixing your problem

Probably not the answer you are looking for but remove the .focus() function entirely.

It produces strange behaviour where if I have one accordion item open and i tab back with Alt + Tab quickly scrolling can be really confusing as it jumps around if you tab quicker than the scroll.

One of the golden rules of accessibility is to only adjusted the scroll position on a page if it is expected (i.e. a return to top button or using in-page anchors).

In the example and on your website once I disabled the ‘scroll to top on focus’ the site actually behaved as expected.

I understand why you did it as occasionally a link that is focused appears off the page, however this remedies itself when you tab again or by scrolling down (your site is logical so that if I tab and my focus is not visible I know it is off the page.)

This tends to happen (items not scrolling into view) when the item is just out of sight, by a px or two, it is common and ironically now falls into ‘expected’ behaviour (another rule, follow accepted and expected behaviour when designing components and pages).

If you really want to fix it

In your focus function instead of just scrolling to the top of the page whenever an item is focused, check if it is off the page.

Below is an example function I found (not tested) that you can use to check if the item is in the viewport, if it is then don’t do anything, if it isn’t then do your scroll function.

var isInViewport = function (elem) {
    var bounding = elem.getBoundingClientRect();
    return (
        bounding.top >= 0 &&
        bounding.left >= 0 &&
        bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
};

So roughly (yet again haven’t tested that the correct items are passed in, this is just to give you an idea).

$(".accordion .accordion-item .accordion-heading a").focus(
    function()
    { 
        if(isInViewport(this) === false){
             //item is not in the viewport so scroll it into view
            $('html:not(:animated), body:not(:animated)').animate({
                scrollTop: $(this).offset().top //I would perhaps add a couple of hundred pixels here to make the item appear in a more natural area.
            }, 250); //remove the animation as a further accessibility improvement, animations can be off putting to people with motion or anxiety disorders.
        }
    }
);

This fixes your problem as no mouse user will ever be able to click an item that is off the page so they won’t ever trigger the scroll event that causes the focus issue.

Answer:

You can change the event setting: Instead focus() event you will do a click() event: When you click a link, you’ll scroll up. This will solve the problem of both keyboard navigation and mouse clicking;And this is also more true in terms of accessibility.

$(".accordion .accordion-item .accordion-heading a").click(
function(e)
{
    e.preventDefault();
    $('html:not(:animated), body:not(:animated)').animate({
        scrollTop: $(this).offset().top
    }, 250);
}
);

Don’t forget to change the link setting to a button by adding role=button attribute.
and add aria-expanded attribute.