another aria Modal - focus trap - last focus
Modal
by ian_smithz
HTML
<div id="page">
<div class="menu">
<button class="btn" type="button" id="modal_open">Open the Modal</button>
</div>
<article class="content-area">
<h1>
Demo:<br />
An accessible Modal Window with JavaScript & CSS
</h1>
<p>
<b>Note</b> - There's a new version of this script that you can find on my <a href="https://github.com/scottaohara/accessible_modal_window">github page</a>.
</p>
<p>
This modal window is made with plain old semantic mark-up, CSS and
a very little bit of JavaScript.
</p>
<p>
Check out the <a href="https://codepen.io/scottohara/pen/lIdfv">source code on codepen</a> to see how it works!
</p>
<form action="#">
<label for="test" class="label">Label for Input</label>
<input type="text" id="test" class="input" />
<label for="test2" class="label">Label for Second Input</label>
<input type="text" id="test2" class="input" />
<label for="test3" class="label">Label for Third Input</label>
<input type="text" id="test3" class="input" />
</form>
<p>
Here's a link to the
<a href="https://en.wikipedia.org/wiki/Modal_window">wikipedia entry on modal windows</a>.
</p>
</article> <!-- end .content-area -->
</div> <!-- end #page -->
<div class="modal-overlay" id="modal_window"
aria-hidden="true" role="dialog"
aria-labelledby="modal_title">
<div class="modal-content" id="modal_holder" role="document">
<h1 id="modal_title">Modal Title</h1>
<form>
<label for="name">Name:</label>
<input type="text" id="name" class="input" placeholder="Gimmie ur Name" />
<label for="email">Email:</label>
<input type="email" id="email" class="input" placeholder="Gimmie ur Email" />
...
CSS
/*
Modal Overlay
*/
.modal-overlay {
background:rgba(0,0,0,.8);
height: 100%;
left: 0;
display: flex;
overflow: auto;
padding: 1.5em;
position: fixed;
top: 0;
transition: opacity .2s;
width: 100%;
z-index: -1;
}
.modal-overlay[aria-hidden="true"] {
opacity: 0;
visibility: hidden;
}
.modal-overlay[aria-hidden="false"] {
opacity: 1;
visibility: visible;
z-index: 2;
}
/*
Modal Content Area
*/
.modal-content {
background: #efefef;
margin: auto;
max-width: 800px;
overflow: auto;
overflow-x: hidden;
padding: 1.5em;
position: relative;
transform: scale(.8);
transition: transform .3s;
width: 100%;
}
.modal-overlay[aria-hidden="false"] .modal-content {
transform: scale(1);
}
/*
General Modal Content
*/
#modal-title {
margin-top: 0;
padding-right: 1.25em;
}
/*
Close Modal Button
*/
.btn-close {
background: none;
border: none;
cursor: pointer;
font-family: arial;
font-size: 2em;
font-weight: 800;
line-height: 1;
padding: 0;
position: absolute;
right: .25em;
top: .25em;
z-index: 2;
}
.btn-close:hover {
color: #c12f48;
}
.btn-close:focus {
outline: 1px dotted;
}
/*
This is here purely for demo styling
*/
*, *:after, *:before {
-webkit-box-sizing: border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
font-family: arial;
}
body, html {
background: #fafafa;
color: #450c13;
font-size: 20px;
line-height: 1.3;
margin: 0;
padding: 0;
-moz-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
text-rendering: optimizeLegibility;
}
a {
color: #921c2b;
}
a:hover {
color: #c12f48;
}
.content-area {
margin: auto;
max-width: 900px;
padding: 20px;
}
.menu {
background: #fff;
border-bottom: 1px solid #bbb;
margin: 0;
padding: 12px 8px 0;
text-align: center;
}
input[type="submit"],
input[type="button"] {
-webkit-appearance:none;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
border:...
JavaScript
// helper function to place modal window as the first child
// of the #page node
var m = document.getElementById('modal_window'),
p = document.getElementById('page');
function swap () {
p.parentNode.insertBefore(m, p);
}
swap();
// modal window
(function() {
'use strict';
// list out the vars
var mOverlay = getId('modal_window'),
mOpen = getId('modal_open'),
mClose = getId('modal_close'),
modal = getId('modal_holder'),
allNodes = document.querySelectorAll("*"),
allFocusableNodes = $('page').find(':focusable'),
modalOpen = false,
lastFocus,
i;
console.log(allNodes);
console.log(allFocusableNodes);
// Let's cut down on what we need to type to get an ID
function getId ( id ) {
return document.getElementById(id);
}
// Let's open the modal
function modalShow () {
lastFocus = document.activeElement;
mOverlay.setAttribute('aria-hidden', 'false');
modalOpen = true;
modal.setAttribute('tabindex', '0');
modal.focus();
}
// binds to both the button click and the escape key to close the modal window
// but only if modalOpen is set to true
function modalClose ( event ) {
if (modalOpen && ( !event.keyCode || event.keyCode === 27 ) ) {
mOverlay.setAttribute('aria-hidden', 'true');
modal.setAttribute('tabindex', '-1');
modalOpen = false;
lastFocus.focus();
}
}
// Restrict focus to the modal window when it's open.
// Tabbing will just loop through the whole modal.
// Shift + Tab will allow backup to the top of the modal,
// and then stop.
function focusRestrict ( event ) {
if ( modalOpen && !modal.contains( event.target ) ) {
event.stopPropagation();
modal.focus();
}
}
// Close modal window by clicking on the overlay
/*
mOverlay.addEventListener('click', function( e ) {
if (e.target == modal.parentNode) {
modalClose( e );
}
}, false);
*/
// open modal by btn...