Tips & Tricks Thursday 07: Focus Management

Tips & Tricks Thursday Logo

Grabbing focus for screen reader users is essential to ensure they are aware of important updates or changes on the page, such as error messages or new content. Here are some tips on how to do this effectively:
Note: The code examples are not great, so stay tuned for our next tips on how to improve them.

01. Use Skip Links

  • Skip Navigation: Provide skip links at the top of the page to allow users to bypass navigation and go directly to the main content or specific sections. This helps users with limited motor skills or screen readers access the content faster.
  • Example:
<a href="#main-content" class="skip-link">Skip to main content</a>

02. Ensure Clear and Visible Focus Indicators

  • CSS Styles: Provide a clear, visible outline around focused elements or background changes (buttons, links, form fields) using CSS. This helps users understand where they are on the page.
  • Example:
#error-message:focus {
  outline: 2px solid #f00;
}

03. Maintain Logical Focus Order

    • Logical Sequence: Ensure that the focus follows logical progression, such as left to right and top to bottom and the focus order follows a logical sequence that matches the visual layout and reading order. Use semantic HTML and avoid setting a custom tabindex unless absolutely necessary.
    • Example:
    <nav>
      <a href="#home">Home</a>
      <a href="#about">About</a>
      <a href="#services">Services</a>
      <a href="#contact">Contact</a>
    </nav>
    
    <main>
      <h1 id="home">Welcome to Our Website</h1>
      <section id="about">...</section>
      <section id="services">...</section>
      <section id="contact">...</section>
    </main>

    04. Handle Dynamic Content Updates

      • ARIA Attributes: Use aria-live attributes to announce dynamic content changes. For example, aria-live="assertive" for urgent updates and aria-live="polite" for less critical updates but also ensure that focus doesn’t shift unexpectedly unless necessary.
      • Example:
      <div aria-live="assertive">Error: Please enter a valid email address.</div>

      05. Announce Form Validation Errors

      • Error Handling: When a form validation error occurs, move focus to the first error message and announce it using ARIA live regions.
      • Example:
      const errorMessage = document.getElementById('error-message');
      errorMessage.setAttribute('aria-live', 'assertive');
      errorMessage.focus();

      06. Trap Focus in Modals and Pop-Ups

        • Focus Management: When a modal or pop-up opens, trap the focus within it until the user closes it. This prevents users from inadvertently navigating to elements behind the modal.
        • Example:
        <div id="modal" role="dialog" aria-labelledby="modal-title" aria-describedby="modal-description" aria-modal="true">
          <h2 id="modal-title">Modal Title</h2>
          <p id="modal-description">This is a modal dialog.</p>
          <button id="close-modal">Close</button>
        </div>
        
        <script>
          const modal = document.getElementById('modal');
          const focusableElements = modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
          const firstFocusableElement = focusableElements[0];
          const lastFocusableElement = focusableElements[focusableElements.length - 1];
        
          document.addEventListener('keydown', function(event) {
            if (event.key === 'Tab') {
              if (event.shiftKey) {
                // Shift + Tab
                if (document.activeElement === firstFocusableElement) {
                  event.preventDefault();
                  lastFocusableElement.focus();
                }
              } else {
                // Tab
                if (document.activeElement === lastFocusableElement) {
                  event.preventDefault();
                  firstFocusableElement.focus();
                }
              }
            }
          });
        
          // Set initial focus
          firstFocusableElement.focus();
        </script>

         07. Return Focus After Interactions

          • Focus Restoration: When users interact with elements like dropdowns or dynamic content, ensure that focus returns to a relevant element after the action is completed, e.g. the element that triggered the interaction.
          • Example:
          <button id="open-modal">Open Modal</button>
          
          <script>
            const openModalButton = document.getElementById('open-modal');
            const closeModalButton = document.getElementById('close-modal');
          
            openModalButton.addEventListener('click', function() {
              modal.style.display = 'block';
              closeModalButton.focus();
            });
          
            closeModalButton.addEventListener('click', function() {
              modal.style.display = 'none';
              openModalButton.focus();
            });
          </script>

          08. Use tabindex for Focus Management

            • Tabindex Attribute: Use tabindex="-1" to make non-focusable elements focusable via JavaScript.
            • Example:
            <div id="error-message" tabindex="-1">Error: Please enter a valid email address.</div>

            09. Manage Focus with JavaScript

            • Focus Method: Use JavaScript to programmatically set focus to important elements, such as error messages or newly revealed content.
            • Example:
            document.getElementById('error-message').focus();

            10. Manage Focus for Single Page Apps (SPA)

            • Focus Management: In SPAs, when content changes without a full page reload, ensure focus shifts to newly loaded content when navigating between views to keep users engaged.
            • Example:
            <nav>
              <a href="#home" onclick="navigate('home')">Home</a>
              <a href="#about" onclick="navigate('about')">About</a>
              <a href="#services" onclick="navigate('services')">Services</a>
              <a href="#contact" onclick="navigate('contact')">Contact</a>
            </nav>
            <main id="main-content">
              <section id="home">...</section>
              <section id="about">...</section>
              <section id="services">...</section>
              <section id="contact">...</section>
            </main>
            
            <script>
              function navigate(sectionId) {
                const sections = document.querySelectorAll('main > section');
                sections.forEach(section => section.style.display = 'none');
                document.getElementById(sectionId).style.display = 'block';
                document.getElementById(sectionId).focus();
              }
            
              // Set initial focus
              document.getElementById('home').focus();
            </script>

            11.  Avoid Focus Loss on Page Reloads

              • Preserve Focus: After submitting forms or updating pages preserve the focus state and return the focus to the next logical element, not the top of the page. Use JavaScript or HTML autofocus attributes if necessary.
              • Example:
              <script>
                window.addEventListener('beforeunload', function() {
                  const activeElementId = document.activeElement.id;
                  localStorage.setItem('activeElementId', activeElementId);
                });
              
                window.addEventListener('load', function() {
                  const activeElementId = localStorage.getItem('activeElementId');
                  if (activeElementId) {
                    const elementToFocus = document.getElementById(activeElementId);
                    if (elementToFocus) {
                      elementToFocus.focus();
                    }
                  }
                });
              </script>

              12. Additional Tips

                • Page Regions: Focus states should correspond to page regions (e.g., header, main, nav, footer), allowing users to navigate logically. When a user moves between regions (e.g., from navigation to main content), focus should be properly managed to maintain consistency with the page structure.
                • Testing: Regularly test your implementation with screen readers and keyboard navigation to ensure that focus management works as intended and that users are properly informed of important updates.
                Scroll to Top