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
you could dynamically add With
<div id="div1" tabindex="-1"> content you'd like to prevent tab</div>
with js logicTo render the question more complete, for a modal dialog, you need two things:
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.
In other words this would guide us to
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: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 triggeredfocus
will be fired afterfocusout
, and the latter is not cancelable.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.