an accessible modal window with aria support written with vanilla javascript
Created
September 16, 2014 12:34
-
-
Save anonymous/8f69eee216e053658f8e to your computer and use it in GitHub Desktop.
A Pen by Scott.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> | |
This modal window is made with plain old semantic mark-up, CSS and | |
a very little bit of JavaScript. | |
</p> | |
<p> | |
Check out the source code 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>. | |
</article> <!-- end .content-area --> | |
</div> <!-- end #page --> | |
<div class="modal-overlay" id="modal-dialog" | |
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" /> | |
<input type="submit" class='btn' value="Submit Form" /> | |
</form> | |
<button class="btn-close" id="modal_close" type="button" aria-label="close"> | |
× | |
</button> | |
</div> <!-- end .modal-content --> | |
</div> <!-- end .modal-overlay --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
'use strict'; | |
// list out the vars | |
var mOverlay = getId('modal-dialog'), | |
mOpen = getId('modal_open'), | |
mClose = getId('modal_close'), | |
modal = getId('modal-holder'), | |
modalOpen = false, | |
lastFocus; | |
// 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 ) { | |
document.addEventListener('focus', function( event ) { | |
if ( modalOpen && !modal.contains( event.target ) ) { | |
event.stopPropagation(); | |
modal.focus(); | |
} | |
}, true); | |
} | |
// 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 click/hit | |
mOpen.addEventListener('click', modalShow); | |
// close modal by btn click/hit | |
mClose.addEventListener('click', modalClose); | |
// close modal by keydown, but only if modal is open | |
document.addEventListener('keydown', modalClose); | |
// restrict tab focus on elements only inside modal window | |
window.addEventListener('keypress', focusRestrict); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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: #fdf7f8; | |
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: 0; | |
padding: 0; | |
} | |
.btn:focus, | |
.input:focus, | |
.modal-content:focus { | |
outline: 1px solid #ccc; | |
box-shadow: 0 0 4px #e8a1ad; | |
} | |
.btn { | |
background: #921c2b; | |
border: 1px solid #4d0912; | |
color: #fff; | |
cursor: pointer; | |
font-size: 1em; | |
line-height: 1; | |
margin-bottom: 12px; | |
padding: 16px 24px; | |
position: relative; | |
text-align: center; | |
transition: transform .2s, background .2s; | |
} | |
.btn:hover, .btn:focus { | |
background: #c12f48; | |
transform: scale(1.1); | |
z-index: 3; | |
} | |
label { | |
display: inline-block; | |
font-size: 1.5em; | |
font-weight: bold; | |
margin-bottom: .5em; | |
} | |
.input { | |
border: 1px solid #333; | |
display: block; | |
font-size: 1.4em; | |
margin-bottom: 1em; | |
min-width: 230px; | |
padding: 8px; | |
width: 100%; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment