HTML Element vs ARIA Role
HTML elements and ARIA are used to define various types of interactive elements on a webpage. HTML elements come with built-in keyboard accessibility, roles and states. In ARIA, the keyboard accessibility, roles and states are user-defined.
When an interactive element cannot be made accessible using native HTML, then ARIA comes into picture.
Usually, either HTML or ARIA or both CAN be used for defining elements on a webpage. However, due to inbuilt functionalities or baked in accessibility, native HTML is given precedence over ARIA for simple elements.
When dynamic or complex functionality appears, ARIA is preferred over native HTML or a mixture of both is used since it provides flexibility for developers. For more information refer to Dynamic Update, Autocomplete, Critical Alerts, Progress Alerts, and Notifications components.
Some examples where ARIA is preferred over HTML are carousel, listbox, application menu, editable combobox and so on. For more information on these examples, refer to Modal Dialog, Content Slider, Toggle Buttons, Tabbed Interface, Tooltips & Toggletips, Collapsible Sections, Multiselect box, Dual Listbox, and Menu and Menu Buttons components.
NOTE:
- New to accessibility or uncertain of requirements, it will be helpful to review all sections below.
- Already familiar with requirements, skip to the “Working Example” section for sample HTML, CSS and JavaScript (when needed), along with a working demo.
- Use native HTML over ARIA for defining elements on a webpage.
-
Use ARIA ONLY when native HTML DOES NOT provide the required
features.
- ARIA role, state and properties MUST be appropriately provided and managed for the elements.
- In case of custom components, ensure keyboard accessibility is implemented.
- Use semantic HTML markup for implementing the elements on webpage.
Native HTML example
<button>Explore more about organization</button>
ARIA example
<!-- Visual label -->
<span class="listbox-label" id="listbox_label">Choose your courses:</span>
<!-- Code for the multiselect box -->
<ul id="list-items" tabindex="0" role="listbox" aria-multiselectable="true" aria-labelledby="listbox_label">
<li id="one" role="option" aria-selected="false">General Chemistry</li>
<li id="two" role="option" aria-selected="false">Physics</li>
<li id="three" role="option" aria-selected="false">General Biology</li>
<li id="four" role="option" aria-selected="false">Financial Accounting</li>
<li id="five" role="option" aria-selected="false">Psychology</li>
(...)
</ul>
A well-defined element majorly benefit the below users.
- People with cognitive disabilities
- People with visual disabilities
- People using keyboard only
- People with limited dexterity
<form id="addressForm" autocomplete="off">
<label for="city">City:</label>
<input type="text" id="city" name="city">
<label for="state">State:</label>
<select id="state" name="state">
<option value="" disabled selected>Select state</option>
<option value="Alabama">Alabama</option>
<option value="Alaska">Alaska</option>
<option value="Arizona">Arizona</option>
<option value="Arkansas">Arkansas</option>
<option value="California">California</option>
<option value="Colorado">Colorado</option>
<option value="Connecticut">Connecticut</option>
<option value="Delaware">Delaware</option>
<option value="Florida">Florida</option>
</select>
<label for="zipCode">Zip Code:</label>
<input type="text" id="zipCode" name="zipCode">
<label for="cb1-input">Preferred Language:</label>
<div class="combobox combobox-list">
<div class="group">
<input id="cb1-input" class="cb_edit" type="text" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="cb1-listbox">
<button type="button" id="cb1-button" tabindex="-1" aria-label="Preferred Language" aria-expanded="false" aria-controls="cb1-listbox">
<svg width="18" height="16" aria-hidden="true" focusable="false" style="forced-color-adjust: auto">
<polygon class="arrow" stroke-width="0" fill-opacity="0.75" fill="currentcolor" points="3,6 15,6 9,14"></polygon>
</svg>
</button>
</div>
<ul id="cb1-listbox" role="listbox" aria-label="Languages">
<li id="lb1-english-us" role="option">English - US</li>
<li id="lb1-english-uk" role="option">English - UK</li>
<li id="lb1-spanish" role="option">Spanish</li>
<li id="lb1-french" role="option">French</li>
</ul>
</div>
<fieldset class="radio-group">
<legend>Are you a US resident?</legend>
<div>
<input type="radio" id="yesResident" name="resident" value="yes">
<label for="yesResident">Yes</label>
</div>
<div>
<input type="radio" id="noResident" name="resident" value="no">
<label for="noResident">No</label>
</div>
</fieldset>
<button class="button-primary" onclick="submitForm()">Submit</button>
</form>
<!-- Modal Dialog HTML -->
<div id="myModal" tabindex="-1" class="modal" role="dialog" aria-labelledby="modalTitle" aria-modal="true" aria-describedby="modal-content">
<div class="modal-content" id="modal-content">
<button class="close" onclick="closeModal()" role="button" tabindex="0" aria-label="Close">×</button>
<h2 id="modalTitle">Following Information is Submitted</h2>
<!-- content goes here -->
</div>
</div>
body {
background-color: #f4f4f4;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
height: 100vh;
& * {
box-sizing: border-box;
}
}
form {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
padding: 20px;
}
label {
display: block;
margin-bottom: 8px;
}
input,
select {
width: 100%;
padding: 8px;
margin-bottom: 16px;
box-sizing: border-box;
}
.button-container {
grid-column: span 2;
text-align: center;
}
button.button-primary {
background-color: #003057;
border: 2px solid #000;
border-radius: 10px;
color: #fff;
display: inline-block;
line-height: 1.15;
padding: 0.35rem 1rem;
width: 50%;
text-align: center;
text-decoration: none;
cursor: pointer;
margin: 0 12px 0 12px;
font-size: 1rem;
vertical-align: middle;
grid-column-end: span 2;
justify-self: center;
}
button.button-primary:hover,
button.button-primary:focus {
background-color: #068379;
outline-offset: 2px;
outline: 1px solid #101010;
}
.dropdown {
position: relative;
display: inline-block;
justify-self: start;
width: 100%;
}
#dropdownButton {
width: 100%;
text-align: left;
padding: 8px 4px;
background-color: #fff;
cursor: pointer;
display: flex;
justify-content: space-between;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
padding: 10px;
z-index: 1;
max-height: 150px;
overflow-y: auto;
width: 100%;
}
.dropdown-content [role="option"] {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
padding: 8px 0;
margin: 0 0 4px;
width: 100%;
}
.dropdown-content [role="option"] input[type="checkbox"] {
text-align: left;
width: 30px;
margin: 4px;
}
.checkbox { margin-right: 8px; }
.arrow-down::after {
content: "\25BC"; /* Unicode character for downward arrow */
margin-left: 5px;
}
.arrow-up::after {
content: "\25B2"; /* Unicode character for upward arrow */
margin-left: 5px;
}
fieldset.radio-group {
display: flex;
flex-direction: row;
align-items: center;
grid-column-end: span 2;
width: 100%;
border: medium none;
text-align: center;
justify-content: center;
}
fieldset.radio-group div {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin: 4px 12px;
}
fieldset.radio-group label,
fieldset.radio-group input {
margin: 0 2px;
}
.combobox-list { position: relative; }
.combobox .group {
display: inline-flex;
padding: 4px;
cursor: pointer;
width: 100%;
border: 2px solid transparent;
}
.combobox input,
.combobox button {
background-color: white;
color: black;
box-sizing: border-box;
padding: 8px 0;
width: 100%;
margin: 0;
vertical-align: bottom;
border: 1px solid #000;
position: relative;
cursor: pointer;
}
.combobox input {
width: 150px;
border-right: none;
outline: none;
font-size: 87.5%;
padding: 1px 3px;
}
.combobox button {
width: 19px;
border-left: none;
outline: none;
color: #000;
}
.combobox button[aria-expanded="true"] svg {
transform: rotate(180deg) translate(0, -3px);
}
ul[role="listbox"] {
margin: 0;
padding: 0;
position: absolute;
left: 4px;
top: 34px;
list-style: none;
background-color: white;
display: none;
box-sizing: border-box;
border: 2px currentcolor solid;
max-height: 250px;
width: 168px;
overflow: scroll;
overflow-x: hidden;
font-size: 87.5%;
cursor: pointer;
}
ul[role="listbox"] li[role="option"] {
margin: 0;
display: block;
padding-left: 3px;
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #bdbdbd;
}
/* focus and hover styling */
.combobox .group:focus-within,
.combobox .group:hover {
border: 2px solid currentcolor;
border-radius: 4px;
}
.combobox .group:focus polygon,
.combobox .group:hover polygon {
fill-opacity: 1;
}
.combobox .group input:focus,
.combobox .group button:focus,
.combobox .group input:hover,
.combobox .group button:hover {
background-color: #def;
}
[role="listbox"] [role="option"][aria-selected="true"],
[role="listbox"] [role="option"]:hover {
background-color: #def;
text-decoration: underline;
}
/* Style for the modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
z-index: 1;
}
/* Style for the modal content */
.modal-content {
background-color: #fefefe;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
max-width: 600px;
margin: 10vh auto auto;
position: relative;
}
/* Style for the close button */
.close {
background-color: transparent;
border: medium none;
position: absolute;
top: 10px;
right: 20px;
font-size: 30px;
font-weight: bold;
cursor: pointer;
}
"use strict";
class ComboboxAutocomplete {
constructor(comboboxNode, buttonNode, listboxNode) {
this.comboboxNode = comboboxNode;
this.buttonNode = buttonNode;
this.listboxNode = listboxNode;
this.comboboxHasVisualFocus = false;
this.listboxHasVisualFocus = false;
this.hasHover = false;
this.isNone = false;
this.isList = false;
this.isBoth = false;
this.allOptions = [];
this.option = null;
this.firstOption = null;
this.lastOption = null;
this.filteredOptions = [];
this.filter = "";
let autocomplete = this.comboboxNode.getAttribute("aria-autocomplete");
if (typeof autocomplete === "string") {
autocomplete = autocomplete.toLowerCase();
this.isNone = autocomplete === "none";
this.isList = autocomplete === "list";
this.isBoth = autocomplete === "both";
} else {
// default value of autocomplete
this.isNone = true;
}
this.comboboxNode.addEventListener(
"keydown",
this.onComboboxKeyDown.bind(this)
);
this.comboboxNode.addEventListener(
"keyup",
this.onComboboxKeyUp.bind(this)
);
this.comboboxNode.addEventListener(
"click",
this.onComboboxClick.bind(this)
);
this.comboboxNode.addEventListener(
"focus",
this.onComboboxFocus.bind(this)
);
this.comboboxNode.addEventListener("blur", this.onComboboxBlur.bind(this));
document.body.addEventListener(
"pointerup",
this.onBackgroundPointerUp.bind(this),
true
);
// initialize pop up menu
this.listboxNode.addEventListener(
"pointerover",
this.onListboxPointerover.bind(this)
);
this.listboxNode.addEventListener(
"pointerout",
this.onListboxPointerout.bind(this)
);
// Traverse the element children of domNode: configure each with
// option role behavior and store reference in.options array.
let nodes = this.listboxNode.getElementsByTagName("LI");
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
this.allOptions.push(node);
node.addEventListener("click", this.onOptionClick.bind(this));
node.addEventListener("pointerover", this.onOptionPointerover.bind(this));
node.addEventListener("pointerout", this.onOptionPointerout.bind(this));
}
this.filterOptions();
// Open Button
let button = this.comboboxNode.nextElementSibling;
if (button && button.tagName === "BUTTON") {
button.addEventListener("click", this.onButtonClick.bind(this));
}
}
getLowercaseContent(node) {
return node.textContent.toLowerCase();
}
isOptionInView(option) {
let bounding = option.getBoundingClientRect();
return (
bounding.top >= 0 &&
bounding.left >= 0 &&
bounding.bottom <=
(window.innerHeight || document.documentElement.clientHeight) &&
bounding.right <=
(window.innerWidth || document.documentElement.clientWidth)
);
}
setActiveDescendant(option) {
if (option && this.listboxHasVisualFocus) {
this.comboboxNode.setAttribute("aria-activedescendant", option.id);
if (!this.isOptionInView(option)) {
option.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
} else {
this.comboboxNode.setAttribute("aria-activedescendant", "");
}
}
setValue(value) {
this.filter = value;
this.comboboxNode.value = this.filter;
this.comboboxNode.setSelectionRange(this.filter.length, this.filter.length);
this.filterOptions();
}
setOption(option, flag) {
if (typeof flag !== "boolean") {
flag = false;
}
if (option) {
this.option = option;
this.setCurrentOptionStyle(this.option);
this.setActiveDescendant(this.option);
if (this.isBoth) {
this.comboboxNode.value = this.option.textContent;
if (flag) {
this.comboboxNode.setSelectionRange(
this.option.textContent.length,
this.option.textContent.length
);
} else {
this.comboboxNode.setSelectionRange(
this.filter.length,
this.option.textContent.length
);
}
}
}
}
setVisualFocusCombobox() {
this.listboxNode.classList.remove("focus");
this.comboboxNode.parentNode.classList.add("focus"); // set the focus class to the parent for easier styling
this.comboboxHasVisualFocus = true;
this.listboxHasVisualFocus = false;
this.setActiveDescendant(false);
}
setVisualFocusListbox() {
this.comboboxNode.parentNode.classList.remove("focus");
this.comboboxHasVisualFocus = false;
this.listboxHasVisualFocus = true;
this.listboxNode.classList.add("focus");
this.setActiveDescendant(this.option);
}
removeVisualFocusAll() {
this.comboboxNode.parentNode.classList.remove("focus");
this.comboboxHasVisualFocus = false;
this.listboxHasVisualFocus = false;
this.listboxNode.classList.remove("focus");
this.option = null;
this.setActiveDescendant(false);
}
// ComboboxAutocomplete Events
filterOptions() {
// do not filter any options if autocomplete is none
if (this.isNone) {
this.filter = "";
}
let option = null;
let currentOption = this.option;
let filter = this.filter.toLowerCase();
this.filteredOptions = [];
this.listboxNode.innerHTML = "";
for (let i = 0; i < this.allOptions.length; i++) {
option = this.allOptions[i];
if (
filter.length === 0 ||
this.getLowercaseContent(option).indexOf(filter) === 0
) {
this.filteredOptions.push(option);
this.listboxNode.appendChild(option);
}
}
// Use populated options array to initialize firstOption and lastOption.
let numItems = this.filteredOptions.length;
if (numItems > 0) {
this.firstOption = this.filteredOptions[0];
this.lastOption = this.filteredOptions[numItems - 1];
if (currentOption && this.filteredOptions.indexOf(currentOption) >= 0) {
option = currentOption;
} else {
option = this.firstOption;
}
} else {
this.firstOption = null;
option = null;
this.lastOption = null;
}
return option;
}
setCurrentOptionStyle(option) {
for (let i = 0; i < this.filteredOptions.length; i++) {
let opt = this.filteredOptions[i];
if (opt === option) {
opt.setAttribute("aria-selected", "true");
if (
this.listboxNode.scrollTop + this.listboxNode.offsetHeight <
opt.offsetTop + opt.offsetHeight
) {
this.listboxNode.scrollTop =
opt.offsetTop + opt.offsetHeight - this.listboxNode.offsetHeight;
} else if (this.listboxNode.scrollTop > opt.offsetTop + 2) {
this.listboxNode.scrollTop = opt.offsetTop;
}
} else {
opt.removeAttribute("aria-selected");
}
}
}
getPreviousOption(currentOption) {
if (currentOption !== this.firstOption) {
let index = this.filteredOptions.indexOf(currentOption);
return this.filteredOptions[index - 1];
}
return this.lastOption;
}
getNextOption(currentOption) {
if (currentOption !== this.lastOption) {
let index = this.filteredOptions.indexOf(currentOption);
return this.filteredOptions[index + 1];
}
return this.firstOption;
}
/* MENU DISPLAY METHODS */
doesOptionHaveFocus() {
return this.comboboxNode.getAttribute("aria-activedescendant") !== "";
}
isOpen() {
return this.listboxNode.style.display === "block";
}
isClosed() {
return this.listboxNode.style.display !== "block";
}
hasOptions() {
return this.filteredOptions.length;
}
open() {
this.listboxNode.style.display = "block";
this.comboboxNode.setAttribute("aria-expanded", "true");
this.buttonNode.setAttribute("aria-expanded", "true");
}
close(force) {
if (typeof force !== "boolean") {
force = false;
}
if (
force ||
(!this.comboboxHasVisualFocus &&
!this.listboxHasVisualFocus &&
!this.hasHover)
) {
this.setCurrentOptionStyle(false);
this.listboxNode.style.display = "none";
this.comboboxNode.setAttribute("aria-expanded", "false");
this.buttonNode.setAttribute("aria-expanded", "false");
this.setActiveDescendant(false);
this.comboboxNode.parentNode.classList.add("focus");
}
}
/* combobox Events */
onComboboxKeyDown(event) {
let flag = false,
altKey = event.altKey;
if (event.ctrlKey || event.shiftKey) {
return;
}
switch (event.key) {
case "Enter":
if (this.listboxHasVisualFocus) {
this.setValue(this.option.textContent);
}
this.close(true);
this.setVisualFocusCombobox();
flag = true;
break;
case "Down":
case "ArrowDown":
if (this.filteredOptions.length > 0) {
if (altKey) {
this.open();
} else {
this.open();
if (
this.listboxHasVisualFocus ||
(this.isBoth && this.filteredOptions.length > 1)
) {
this.setOption(this.getNextOption(this.option), true);
this.setVisualFocusListbox();
} else {
this.setOption(this.firstOption, true);
this.setVisualFocusListbox();
}
}
}
flag = true;
break;
case "Up":
case "ArrowUp":
if (this.hasOptions()) {
if (this.listboxHasVisualFocus) {
this.setOption(this.getPreviousOption(this.option), true);
} else {
this.open();
if (!altKey) {
this.setOption(this.lastOption, true);
this.setVisualFocusListbox();
}
}
}
flag = true;
break;
case "Esc":
case "Escape":
if (this.isOpen()) {
this.close(true);
this.filter = this.comboboxNode.value;
this.filterOptions();
this.setVisualFocusCombobox();
} else {
this.setValue("");
this.comboboxNode.value = "";
}
this.option = null;
flag = true;
break;
case "Tab":
this.close(true);
if (this.listboxHasVisualFocus) {
if (this.option) {
this.setValue(this.option.textContent);
}
}
break;
case "Home":
this.comboboxNode.setSelectionRange(0, 0);
flag = true;
break;
case "End":
let length = this.comboboxNode.value.length;
this.comboboxNode.setSelectionRange(length, length);
flag = true;
break;
default:
break;
}
if (flag) {
event.stopPropagation();
event.preventDefault();
}
}
isPrintableCharacter(str) {
return str.length === 1 && str.match(/\S| /);
}
onComboboxKeyUp(event) {
let flag = false,
option = null,
char = event.key;
if (this.isPrintableCharacter(char)) {
this.filter += char;
}
// this is for the case when a selection in the textbox has been deleted
if (this.comboboxNode.value.length < this.filter.length) {
this.filter = this.comboboxNode.value;
this.option = null;
this.filterOptions();
}
if (event.key === "Escape" || event.key === "Esc") {
return;
}
switch (event.key) {
case "Backspace":
this.setVisualFocusCombobox();
this.setCurrentOptionStyle(false);
this.filter = this.comboboxNode.value;
this.option = null;
this.filterOptions();
flag = true;
break;
case "Left":
case "ArrowLeft":
case "Right":
case "ArrowRight":
case "Home":
case "End":
if (this.isBoth) {
this.filter = this.comboboxNode.value;
} else {
this.option = null;
this.setCurrentOptionStyle(false);
}
this.setVisualFocusCombobox();
flag = true;
break;
default:
if (this.isPrintableCharacter(char)) {
this.setVisualFocusCombobox();
this.setCurrentOptionStyle(false);
flag = true;
if (this.isList || this.isBoth) {
option = this.filterOptions();
if (option) {
if (this.isClosed() && this.comboboxNode.value.length) {
this.open();
}
if (
this.getLowercaseContent(option).indexOf(
this.comboboxNode.value.toLowerCase()
) === 0
) {
this.option = option;
if (this.isBoth || this.listboxHasVisualFocus) {
this.setCurrentOptionStyle(option);
if (this.isBoth) {
this.setOption(option);
}
}
} else {
this.option = null;
this.setCurrentOptionStyle(false);
}
} else {
this.close();
this.option = null;
this.setActiveDescendant(false);
}
} else if (this.comboboxNode.value.length) {
this.open();
}
}
break;
}
if (flag) {
event.stopPropagation();
event.preventDefault();
}
}
onComboboxClick() {
if (this.isOpen()) {
this.close(true);
} else {
this.open();
}
}
onComboboxFocus() {
this.filter = this.comboboxNode.value;
this.filterOptions();
this.setVisualFocusCombobox();
this.option = null;
this.setCurrentOptionStyle(null);
}
onComboboxBlur() {
this.removeVisualFocusAll();
}
onBackgroundPointerUp(event) {
if (
!this.comboboxNode.contains(event.target) &&
!this.listboxNode.contains(event.target) &&
!this.buttonNode.contains(event.target)
) {
this.comboboxHasVisualFocus = false;
this.setCurrentOptionStyle(null);
this.removeVisualFocusAll();
setTimeout(this.close.bind(this, true), 300);
}
}
onButtonClick() {
if (this.isOpen()) {
this.close(true);
} else {
this.open();
}
this.comboboxNode.focus();
this.setVisualFocusCombobox();
}
/* Listbox Events */
onListboxPointerover() {
this.hasHover = true;
}
onListboxPointerout() {
this.hasHover = false;
setTimeout(this.close.bind(this, false), 300);
}
// Listbox Option Events
onOptionClick(event) {
this.comboboxNode.value = event.target.textContent;
this.close(true);
}
onOptionPointerover() {
this.hasHover = true;
this.open();
}
onOptionPointerout() {
this.hasHover = false;
setTimeout(this.close.bind(this, false), 300);
}
}
// Initialize comboboxes
window.addEventListener("load", function () {
let comboboxes = document.querySelectorAll(".combobox-list");
for (let i = 0; i < comboboxes.length; i++) {
let combobox = comboboxes[i];
let comboboxNode = combobox.querySelector("input");
let buttonNode = combobox.querySelector("button");
let listboxNode = combobox.querySelector('[role="listbox"]');
new ComboboxAutocomplete(comboboxNode, buttonNode, listboxNode);
}
});
function submitForm() {
let city = document.getElementById("city").value;
let state = document.getElementById("state").value;
let zipCodeInput = document.getElementById("zipCode");
let residentYes = document.getElementById("yesResident");
let residentNo = document.getElementById("noResident");
let zipCode = zipCodeInput.value;
let residentValue = residentYes.checked
? residentYes.value
: residentNo.checked
? residentNo.value
: "";
// Get the selected value from the combobox
let selectedLanguage = document.getElementById("cb1-input").value;
// Build the content for the modal
let modalContent =
'Following Information is Submitted
' +
"City: " +
city +
"
" +
"State: " +
state +
"
" +
"Zip Code: " +
zipCode +
"
" +
"Selected Language: " +
selectedLanguage +
"
" +
"Are you a US resident? " +
residentValue +
"
";
// Set the content of the modal
document.getElementById("modal-content").innerHTML = modalContent;
// Show the modal
openModal();
event.preventDefault();
return false;
}
// Function to open the modal
function openModal() {
document.getElementById("myModal").style.display = "block";
// document.getElementById('myModal').setAttribute('tabindex', '-1');
document.getElementById("myModal").focus();
document.addEventListener("keydown", handleModalKeydown);
}
// Function to close the modal
function closeModal() {
document.getElementById("myModal").style.display = "none";
// document.getElementById('modal-content').removeAttribute('tabindex');
document.removeEventListener("keydown", handleModalKeydown);
}
// Function to handle keyboard events in the modal
function handleModalKeydown(event) {
let modalContent = document.getElementById("myModal");
let focusableElements = modalContent.querySelectorAll(
'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (event.key === "Escape") {
closeModal();
} else if (event.key === "Tab") {
// Trap focus within the modal
let firstElement = modalContent;
let lastElement = focusableElements[focusableElements.length - 1];
if (event.shiftKey) {
if (document.activeElement === firstElement) {
lastElement.focus();
event.preventDefault();
}
} else {
if (document.activeElement === lastElement) {
firstElement.focus();
event.preventDefault();
}
}
}
}