Las ventanas modal accesibles necesitan contar con tres caracteristicas sumamente importantes, la primera es que permitan que al acceder a la ventana el foco entre directo a ella
y no se mantenga fuera; la segunda es que una vez dentro, el foco no pueda salir de la ventana modal hasta cerrarla y finalmente, al cerrar la ventana, es necesario que el foco sea
redirigido al último elemento navegado y no se mueva al inicio o al final de la página.
<div id="mainPage" aria-hidden="false">
<h2>La ventana modal accesible en Acción</h2>
<p><button id="startModal">Ver la ventana Modal</button></p>
</div>
<div id="modal" aria-hidden="true" role="dialog">
<form name="form1" onSubmit="return false;">
<p>
<label for="firstName">Nombre:</label>
<input type="text" name="firstName" id="firstName">
</p>
<p>
<label for="lastName">Apellidos:</label>
<input type="text" name="lastName" id="lastName">
</p>
<p>
<label for="email">Correo Electrónico:</label>
<input type="text" name="email" id="email">
</p>
<p>
<input type="button" name="button" id="enter" value="Enviar">
<input type="button" name="cancelButton" id="cancelButton" value="Cancelar">
<button id="modalCloseButton" class="modalCloseButton" title="Cerrar"><img id="cancel" src="./content/modal_overlay/img/x.png" alt="Cerrar"></button>
</form>
</div>
<div id="modalOverlay" tabindex="-1"></div>
// jQuery formatted selector to search for focusable items
var focusableElementsString = "a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]";
// store the item that has focus before opening the modal window
var focusedElementBeforeModal;
$(document).ready(function() {
jQuery('#startModal').click(function(e) {
showModal($('#modal'));
});
jQuery('#cancel').click(function(e) {
hideModal();
});
jQuery('#cancelButton').click(function(e) {
hideModal();
});
jQuery('#enter').click(function(e) {
enterButtonModal();
});
jQuery('#modalCloseButton').click(function(e) {
hideModal();
});
jQuery('#modal').keydown(function(event) {
trapTabKey($(this), event);
})
jQuery('#modal').keydown(function(event) {
trapEscapeKey($(this), event);
})
});
function trapSpaceKey(obj, evt, f) {
// if space key pressed
if (evt.which == 32) {
// fire the user passed event
f();
evt.preventDefault();
}
}
function trapEscapeKey(obj, evt) {
// if escape pressed
if (evt.which == 27) {
// get list of all children elements in given object
var o = obj.find('*');
// get list of focusable items
var cancelElement;
cancelElement = o.filter("#cancel")
// close the modal window
cancelElement.click();
evt.preventDefault();
}
}
function trapTabKey(obj, evt) {
// if tab or shift-tab pressed
if (evt.which == 9) {
// get list of all children elements in given object
var o = obj.find('*');
// get list of focusable items
var focusableItems;
focusableItems = o.filter(focusableElementsString).filter(':visible')
// get currently focused item
var focusedItem;
focusedItem = jQuery(':focus');
// get the number of focusable items
var numberOfFocusableItems;
numberOfFocusableItems = focusableItems.length
// get the index of the currently focused item
var focusedItemIndex;
focusedItemIndex = focusableItems.index(focusedItem);
if (evt.shiftKey) {
//back tab
// if focused on first item and user preses back-tab, go to the last focusable item
if (focusedItemIndex == 0) {
focusableItems.get(numberOfFocusableItems - 1).focus();
evt.preventDefault();
}
} else {
//forward tab
// if focused on the last item and user preses tab, go to the first focusable item
if (focusedItemIndex == numberOfFocusableItems - 1) {
focusableItems.get(0).focus();
evt.preventDefault();
}
}
}
}
function setInitialFocusModal(obj) {
// get list of all children elements in given object
var o = obj.find('*');
// set focus to first focusable item
var focusableItems;
focusableItems = o.filter(focusableElementsString).filter(':visible').first().focus();
}
function enterButtonModal() {
// BEGIN logic for executing the Enter button action for the modal window
alert('form submitted');
// END logic for executing the Enter button action for the modal window
hideModal();
}
function showModal(obj) {
jQuery('#mainPage').attr('aria-hidden', 'true'); // mark the main page as hidden
jQuery('#modalOverlay').css('display', 'block'); // insert an overlay to prevent clicking and make a visual change to indicate the main apge is not available
jQuery('#modal').css('display', 'block'); // make the modal window visible
jQuery('#modal').attr('aria-hidden', 'false'); // mark the modal window as visible
// save current focus
focusedElementBeforeModal = jQuery(':focus');
// get list of all children elements in given object
var o = obj.find('*');
// Safari and VoiceOver shim
// if VoiceOver in Safari is used, set the initial focus to the modal window itself instead of the first keyboard focusable item. This causes VoiceOver to announce the aria-labelled attributes. Otherwise, Safari and VoiceOver will not announce the labels attached to the modal window.
// set the focus to the first keyboard focusable item
o.filter(focusableElementsString).filter(':visible').first().focus();
}
function hideModal() {
jQuery('#modalOverlay').css('display', 'none'); // remove the overlay in order to make the main screen available again
jQuery('#modal').css('display', 'none'); // hide the modal window
jQuery('#modal').attr('aria-hidden', 'true'); // mark the modal window as hidden
jQuery('#mainPage').attr('aria-hidden', 'false'); // mark the main page as visible
// set focus back to element that had it before the modal was opened
focusedElementBeforeModal.focus();
}
#modalOverlay {
width:100%;
height:100%;
z-index:2; /* places the modal overlay between the main page and the modal dialog*/
background-color:#000;
opacity:0.5;
position:fixed;
top:0;
left:0;
display:none;
margin:0;
padding:0;
}
#modal {
width:50%;
margin-left:auto;
margin-right:auto;
padding: 5px;
border: thin #000 solid;
background-color:#fff;
z-index:3; /* places the modal dialog on top of everything else */
position:fixed;
top:25%;
left:25%;
display:none;
}
#modal h1 {
text-align:center;
}
.modalCloseButton {
float:right;
position:absolute;
top:10px;
left:92%;
height:25px;
}
.modalCloseButton img {
border:0;
}
.screen-reader-offscreen {
position:absolute;
left:-999px;
width:1px;
height:1px;
top:auto;
}