Solution 1 :

Thanks for answering the questions, hopefully the following explanation will highlight why I was asking them (and then I will offer some solutions).

Why can’t we just intercept the tab key?

Screen reader users don’t navigate using only the tab key. Depending on the screen reader they use they use different shortcuts to navigate via headings, links, forms etc.

This causes accessibility issues with popups as people only tend to capture the tab key. Then if a user uses a shortcut, e.g. 2 in NVDA to jump through heading level 2s on the page they can end up outside of the modal without knowing it exists, often without any way to get back into the modal without tabbing around for ages.

So the solution is obvious, make sure nothing else on the page is accessible (not just not focusable).

However you need to have your DOM structure well ordered / organised to make this manageable.

Problems to solve

  1. Screen reader users can access non-focusable elements
  2. They could change their shortcut keys so we can’t rely on intercepting key presses to try and fix the problem.
  3. We want to maintain the same visual design (i.e. we can’t just use display:none on all other elements).
  4. We want a pattern we can repeat so we can’t just individually hide elements on the page.
  5. We want to manage focus correctly so that when the modal is closed it reverts focus back to the previous item (in your circumstances).
  6. We want to loop back to the first item in the modal upon reaching the last item (we can do this intercepting the tab key as we can’t cover all scenarios, neither do we want to as that would cause more accessibility issues.)

Solution

problems 1, 2, 3 and 4

As we cannot intercept key presses to manage focus within the modal we have to make every other element (other than those in the modal) completely inaccessible while the modal is active.

aria-hidden="true" is effectively display: none for screen readers. Support for aria-hidden is good at around 90% to 95% for all screen reader / browser combos.

To make the content outside of the modal inaccessible we need to add aria-hidden="true" to every element outside of the modal as well as tabindex="-1" to ensure that nothing can be focused outside of the modal using the tab key.

I asked about your document structure as the easiest way to implement this is on regions / main landmarks.

So when the modal is active we need to add aria-hidden="true" and tabindex="-1" to the <head>, <main>, <footer> etc. By doing it at the landmark level and by putting the modal outside of the main document flow this becomes easy to manage and maintain while preserving semantic HTML markup. The opposite is true of the modal (so hide it using the same technique when it isn’t active.)

Before modal open

<head aria-hidden="false"></head>
<main aria-hidden="false"></main>
<footer aria-hidden="false"></footer>
<div class="modal" aria-hidden="true" tabindex="-1"></div>

Modal open

<head aria-hidden="true" tabindex="-1"></head>
<main aria-hidden="true" tabindex="-1"></main>
<footer aria-hidden="true" tabindex="-1"></footer>
<div class="modal" aria-hidden="false"></div>

Note how I have aria-hidden always added as some screen readers do not react well to dynamic addition of aria (they react fine to changing properties though).

Points 5 and 6

For this I think it will be easiest to share the code I use to trap focus within a modal.

The purpose of the below function is to focus the first focusable item within a modal when it opens, store a reference to the element that activated the modal (as we want to return the user there when the modal closes) and to manage focus.

Please note that I use a micro library to enable jQuery style selectors so you may need to tweak things for your use.

Managing focus explanation and code

The item variable is the referring button that was pressed before opening the modal (so we can return focus there after closing the modal).

The className variable is the class name of the modal so you can target different modals.

kluio.helpers is just an array of functions I use across the site so can be omitted.

kluio.globalVars is an array of global variables so could be substituted for returning the results from the function.

I have added comments to each part to explain what it does.

The setFocus function is called when the modal is opened passing in the element that was pressed to activate it and the modal’s className (works for our use case better, you could use an ID instead).

var kluio = {};
kluio.helpers = {};
kluio.globalVars = {};

kluio.helpers.setFocus = function (item, className) { //we pass in the button that activated the modal and the className of the modal, your modal must have a unique className for this to work.

    className = className || "content"; //defaults to class 'content' in case of error ("content" being the class on the <main> element.)
    kluio.globalVars.beforeOpen = item; //we store the button that was pressed before the modal opened in a global variable so we can return focus to it on modal close.

    var focusableItems = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', '[tabindex="0"]']; //a list of items that should be focusable.
    var findItems = [];
    for (i = 0, len = focusableItems.length; i < len; i++) {
        findItems.push('.' + className + " " + focusableItems[i]); //add every focusable item to an array.
    }

    var findString = findItems.join(", ");
    kluio.globalVars.canFocus = Array.prototype.slice.call($('body').find(findString)); //please note we use a custom replacement for jQuery, pretty sure .find() behaves identically but just check it yourself.
    if (kluio.globalVars.canFocus.length > 0) {
        setTimeout(function () { //set timeout not needed most of the time, we have a modal that is off-screen and slides in, setting focus too early results in the page jumping so we added a delay, you may be able to omit this.
            kluio.globalVars.canFocus[0].focus(); //***set the focus to the first focusable element within the modal
            kluio.globalVars.lastItem = kluio.globalVars.canFocus[kluio.globalVars.canFocus.length - 1]; //we also store the last focusable item within the modal so we can keep focus within the modal. 
        }, 600);
    }
}

We then intercept the keydown event with the following function to manage focus.

document.onkeydown = function (evt) {
    evt = evt || window.event;
    if (evt.keyCode == 27) {
        closeAllModals(); //a function that will close any open modal with the Escape key
    }
    if (kluio.globalVars.modalOpen && evt.keyCode == 9) { //global variable to check any modal is open and key is the tab key
        if (evt.shiftKey) { //also pressing shift key
            if (document.activeElement == kluio.globalVars.canFocus[0]) { //the current element is the same as the first focusable element
                evt.preventDefault();
                kluio.globalVars.lastItem.focus(); //we focus the last focusable element as we are reverse tabbing through the items.
            }
        } else {
            if (document.activeElement == kluio.globalVars.lastItem) { //when tabbing forward we look for the last tabbable element 
                evt.preventDefault();
                kluio.globalVars.canFocus[0].focus(); //move the focus to the first tabbable element.
            }
        }
    }
};

Finally in your version of the closeAllModals function you need to return focus to the referring element / button.

if (kluio.globalVars.beforeOpen) {
    kluio.globalVars.beforeOpen.focus();
}

The line kluio.globalVars.canFocus[0].focus(); is called to set focus to the first focusable item within the modal once it is activated, you shouldn’t need to tab into the first element when it opens it should be automatically focused.

Point 5 is covered by the line kluio.globalVars.beforeOpen = item; to set a reference to the item that opened the modal and kluio.globalVars.beforeOpen.focus(); within the close function to return focus to that item.

Point 6 is covered within the document.onkeydown function starting at if (kluio.globalVars.modalOpen && evt.keyCode == 9) {.

I hope all of the above is clear, any questions just ask, if I have time later I will turn it all into a fiddle.

Problem :

I have a form with 3 input element. the accessibility team requires to focus the element on tab press. it works well. event doing well by shift+tab as well. but the issue is after the submit button reached by focus they want to continue from first input element instead of leaving the page and focusing the address bar.

Is it possible? how can i make focus to loop my form in tab and shif+tab instead of moving out?

I am showing the form in popup modal.

<form action="/action_page.php">
  <label for="fname">First name:</label><br>
  <input tabindex="1" type="text" id="fname" name="fname" value="John"><br>
  <label for="lname">Last name:</label><br>
  <input tabindex="2" type="text" id="lname" name="lname" value="Doe"><br><br>
  <input tabindex="3" type="submit" value="Submit">
</form> 
  1. Does this popup appear on page load or is it activated using a button on the page? => button click show the popup( for confirm )
  2. Where is the popup located within the DOM – is it within a / etc. or is it outside of the document flow? => within dom (placed as angular component )

2a. If it is within the / etc. are you able to move it outside of that.
=> it’s sitting outside out it already. because the popup page based

  1. Do they want a fully accessible version or is tab the only requirement (as screen reader users do not tend to use tab to navigate, instead using shortcuts for headings, links, forms mode etc.). Sorry lots of questions just need to know how in-depth to go in answer. => required as fully accessible

Comments

Comment posted by stackoverflow.com/questions/14314659/…

I hope it may helps you-

Comment posted by Graham Ritchie

Few questions. 1. Does this popup appear on page load or is it activated using a button on the page? 2. Where is the popup located within the DOM – is it within a

Comment posted by 3gwebtrain

@GrahamRitchie – I have updated my questions with answering your questions.

Comment posted by 3gwebtrain

Is there a way to query by

Comment posted by Graham Ritchie

what do you mean?

Comment posted by 3gwebtrain

I would like to select all element in page which has the property

Comment posted by Graham Ritchie

You can just use a CSS selector and

By