skip to Main Content

I have created the below JavaScript code for a popup that needs to be accessible.

The problem is that during keyboard-browsing the tab button selects all the buttons behind the popup on the landing page first. I need it to only select the buttons on the popup and the close button.

I understand that focus trapping the popup is a method that might work but I don’t know how to add that to my code.

https://codepen.io/aryanotstark/pen/KKBjLXY << Here is the code

https://digitalcloud.co.za/kiron/ << You can view the popup in action on this website

<script>
    // Check if the popup has already been shown during the current session
    if (!sessionStorage.getItem('popupShown')) {
        setTimeout(function() {
            var popup = document.createElement("div");
            popup.style.position = "fixed";
            popup.style.top = "0";
            popup.style.left = "0";
            popup.style.zIndex = "999";
            popup.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
            popup.style.width = "100vw";
            popup.style.height = "100vh";
            popup.style.display = "flex";
            popup.style.alignItems = "center";
            popup.style.justifyContent = "center";
            var innerPopup = document.createElement("div");
            innerPopup.style.backgroundColor = "black";
            innerPopup.style.display = "flex";
            innerPopup.style.flexDirection = "column";
            innerPopup.style.alignItems = "center";
            innerPopup.style.justifyContent = "center";
            innerPopup.style.padding = "40px";
            innerPopup.style.margin = "35px";
            innerPopup.style.borderRadius = "25px";

            var img = document.createElement("img");
img.src = "https://digitalcloud.co.za/wp-content/uploads/2023/01/kerridge_pop_up_illustration.png ";
img.alt = "Find out about Kerridge’s core solution ";
img.style.width = "25%";
img.style.cursor = "pointer";
innerPopup.appendChild(img);

            var text = document.createElement("p");
            text.innerHTML = "Interested to find out more about our core solution?";
            text.style.color = "white";
            text.style.marginTop = "50px";
            text.style.textAlign = "center";
            text.style.fontSize = "20px";
            innerPopup.appendChild(text);

            var button = document.createElement("button");
            button.innerHTML = "Book a free demo today";
            button.style.backgroundColor = "#E8017B";
            button.style.color = "white";
            button.style.fontSize = "20px";
            button.style.padding = "15px";
            button.style.borderRadius = "50px"
            button.style.border = "none"
            button.onclick = function() {
                location.href = "https://digitalcloud.co.za/ ";
            }
            innerPopup.appendChild(button);
            
            var secondText = document.createElement("p");
            secondText.innerHTML = "Stay up to date with the latest Kerridge product updates and existing announcements";
            secondText.style.color = "white";
            secondText.style.marginTop = "50px";
            secondText.style.textAlign = "center";
            secondText.style.fontSize = "20px";
            innerPopup.appendChild(secondText);
            
            var secondButton = document.createElement("button");
                secondButton.innerHTML = "Sign up to our Newsletter";
                secondButton.style.color = "#E8017B";
                secondButton.style.background = "transparent";
                secondButton.style.border = "none";
                secondButton.style.fontSize = "20px";
                secondButton.onclick = function() {
                 location.href = "https://digitalcloud.co.za/ ";
                }
                
                innerPopup.appendChild(secondButton);

            var closeBtn = document.createElement("div");
            closeBtn.style.position = "absolute";
            closeBtn.style.top = "50px";
            closeBtn.style.right = "50px";
            closeBtn.style.cursor = "pointer";
            closeBtn.style.width = "60px";
            closeBtn.style.height = "60px";
            closeBtn.style.borderRadius = "50%";
            closeBtn.style.backgroundColor = "#E8017B";
            closeBtn.style.display = "flex";
            closeBtn.style.alignItems = "center";
            closeBtn.style.justifyContent = "center";

closeBtn.setAttribute("tabindex", "0");
closeBtn.setAttribute("role", "button");
closeBtn.setAttribute("aria-label", "Close");

closeBtn.addEventListener("click", function() {
  // Add logic to close something here
  popup.remove();
});

closeBtn.addEventListener("keydown", function(event) {
  if (event.key === "Enter" || event.key === " ") {
    // Add logic to close something here
    popup.remove();
  }
});

            var closeX = document.createElement("div");
            closeX.innerHTML = "X";
            closeX.style.color = "white";
            closeX.style.fontWeight = "bold";

            closeBtn.appendChild(closeX);
            closeBtn.onclick = function() {
                popup.remove();
            }
            innerPopup.appendChild(closeBtn);

            popup.appendChild(innerPopup);
            document.body.appendChild(popup);
            // Set a value in sessionStorage indicating that the popup has been shown
            sessionStorage.setItem('popupShown', 'true');
        }, 90000 );
    }
</script>

2

Answers


  1. you could dynamically add With <div id="div1" tabindex="-1"> content you'd like to prevent tab</div> with js logic

    Login or Signup to reply.
  2. To render the question more complete, for a modal dialog, you need two things:

    1. Trap focus inside the modal dialog
    2. Hide all other contents from assistive technology

    Of course there is more to it, but this is the part relevant to the question.

    Navigation via Tab is only one means of navigation with screen readers. TalkBack and VoiceOver, the screen readers on Android and iOS, make no difference between real focus and reading focus. That is what the latter is about: Avoiding reading access to contents outside the dialog.

    In a lot of solutions this is badly done, and one can move outside a modal dialog on mobile platforms, for example in Bootstrap 5.

    Native, standards methods

    The <dialog> element was standardised to solve these issues.

    Obviously, this is the best way to go, since it means you have all the momentum of browser vendors’ development in favour of your dialog.

    According to Scott O’hara two weeks ago, you should Use it

    If you don’t want to rely on this native element, and implement a custom modal dialog, there is two things the native one does, concerning your question.

    <dialog> elements invoked by the showModal() method will have an implicit aria-modal="true" […]

    […] everything other than the <dialog> and its contents should be rendered inert using the inert attribute.

    In other words this would guide us to

    <main inert>
      …
      <p><a href="#">Can you focus me?</a></p>
    </main>
    <div role="dialog" aria-modal="true" aria-labelledby="d-title">
      <h2 id="d-title">Dialog title</h2>
      …
      <p><button>Can you focus me?</button></p>
    </div>

    Unfortunately, also here, browser support is not great yet.

    Alternative for aria-modal

    That attribute is supposed to hide everything else (the <main> in above example) from assistive technology. For browsers that don’t support this, aria-hidden can be applied to the other contents:

    <main aria-hidden="true">
    …
    </main>
    <div role="dialog" …>
    

    This has no effect on focus, but on reading through assistive technology.

    Trap focus

    There are two strategies.

    Intercept focusin

    This is the mothed used in Bootstrap’s Modal and the APG’s Modal Dialog Example.

    One strategy is to bind a listener to the focusin event of contents outside the dialog, and put focus back on the first or last interactive element in the dialog, depending on which one was last focused.

    You cannot use the focusout event because the Focus order of user triggered focus will be fired after focusout, and the latter is not cancelable.

    document.getElementsByTagName('main')[0].addEventListener('focusin', () => document.querySelector('[role=dialog] button').focus());
    <main>
      …
      <p><a href="#">Can you focus me?</a></p>
    </main>
    <div role="dialog" aria-modal="true" aria-labelledby="d-title">
      <h2 id="d-title">Dialog title</h2>
      …
      <p><button autofocus>Can you focus me?</button></p>
    </div>

    Make everything else not focusable

    You can try to render all other interactive elements not focusable or clickable.

    This is obviously more fragile, especially if the interactivity is not exposed through use of native HTML elements or ARIA roles, so it becomes hard to identify these.

    <main style="pointer-events: none">
      …
      <p><a href="#" tabindex="-1">Can you focus me?</a></p>
    </main>
    <div role="dialog" aria-modal="true" aria-labelledby="d-title">
      <h2 id="d-title">Dialog title</h2>
      …
      <p><button autofocus>Can you focus me?</button></p>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search