Last active
August 31, 2017 17:53
-
-
Save dfkaye/31939cd9fd3d00be68ffd5ab42622a35 to your computer and use it in GitHub Desktop.
wai-aria-alertdialog per w3c best practice spec at https://www.w3.org/TR/wai-aria-practices/#alertdialog
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
<meta name="HandheldFriendly" content="true" /> | |
<title>WAI-ARIA Alert Dialog (Modal)</title> | |
<style> | |
:root { | |
font-size: 100%; | |
font-family: verdana, sans-serif; | |
line-height: 1.6; | |
margin: 1em; | |
padding: 1em; | |
} | |
html { | |
box-sizing: border-box; | |
} | |
body { | |
background: #eee; | |
} | |
*, *:before, *:after { | |
box-sizing: inherit; | |
} | |
body * + * { | |
margin: 1.5rem, 1.25rem, 1.25rem; | |
} | |
[aria-hidden="true"] { | |
display: none; | |
} | |
ul { | |
margin: 0 0 1rem 0; | |
padding: 0; | |
} | |
li { | |
margin: 0 0 0 1rem; | |
padding: 0; | |
} | |
abbr { | |
border-bottom: 1px dotted blue; | |
text-decoration: none; | |
} | |
kbd { | |
font-size: 1rem; | |
} | |
/** alertdialog rules **/ | |
[role="alertdialog"] { | |
background: #fff; | |
border: 1px solid #ccc; | |
border-radius: 0.25em; | |
padding: 1.5em 2em 2em; | |
position: fixed; | |
/* pseudo-responsive settings */ | |
bottom: 10%; | |
left: 10%; | |
right: 10%; | |
top: 10%; | |
} | |
/** | |
* Hide all subsequent siblings of alertdialog, which is a direct child of | |
* the body element. | |
*/ | |
[open] ~ * { | |
visibility: hidden; | |
} | |
</style> | |
</head> | |
<body> | |
<dialog role="alertdialog" aria-hidden="true" aria-labelledby="dialogue-label"> | |
<!-- We make the alertdialog a direct child of the body, so we can hide its | |
siblings when opened. The "dialogue-label" element is generated when | |
the user opens the dialog, and placed inside the dialog itself. --> | |
</dialog> | |
<header role="banner"> | |
<h1> | |
<abbr title="Web Accessibility Initiative">WAI</abbr><span>–</span><abbr title="Accessible Rich Internet Applications">ARIA</abbr> | |
Alert Dialog (Modal)</h1> | |
<p> | |
… per <abbr title="World-Wide Web Consortium">W3C</abbr> best | |
practice spec at | |
<a href="https://www.w3.org/TR/wai-aria-practices/#alertdialog"> | |
https://www.w3.org/TR/wai-aria-practices/#alertdialog</a>. | |
</p> | |
</header> | |
<main role="main"> | |
<section> | |
<h2>Alert Dialog Behavior</h2> | |
<p> | |
Focus is maintained in the dialog while it is open, i.e., the | |
<kbd>Tab</kbd> key is cancelled when user tab's out of the last | |
element (button) in the dialog. | |
</p> | |
<p> | |
Focus is returned to the element (button) that initiated the dialog. | |
</p> | |
<p> | |
To open the dialog, navigate to the <kbd>Open</kbd> button, then | |
either click it, or press the <kbd>Space</kbd> bar or | |
<kbd>Enter</kbd> key. | |
</p> | |
<p> | |
To close the dialog, either press <kbd>Escape</kbd> key, or navigate | |
to the <kbd>Close</kbd> button, then either click it, or press the | |
<kbd>Space</kbd> bar or <kbd>Enter</kbd> key. | |
</p> | |
<button type="button" data-dialog-message="Click button to close the dialog"> | |
Open | |
</button> | |
</section> | |
</main> | |
<footer role="contentinfo"> | |
© 2017 | |
</footer> | |
<script> | |
(function() { | |
"use strict"; | |
var dialog = document.querySelector('[role="alertdialog"]'); | |
var handleClick = function(e) { | |
var target = e.target; | |
var message = target.getAttribute('data-dialog-message'); | |
if (message) { | |
handleOpen(target, message); | |
} | |
}; | |
var handleClose = function() { | |
dialog.parentNode.removeEventListener('keydown', handleEscape); | |
dialog.removeAttribute('open'); | |
dialog.setAttribute('aria-hidden', 'true'); | |
[].slice.call(dialog.querySelectorAll('button')).map(function(button) { | |
button.removeEventListener('click', handleClick); | |
button.removeEventListener('click', handleRefocus); | |
}); | |
while (dialog.firstChild) { | |
dialog.removeChild(dialog.firstChild); | |
} | |
dialog.opener.focus(); | |
dialog.opener = undefined; | |
}; | |
var handleEscape = function(e) { | |
var k = e.which || e.keyCode; | |
if (k == '27') { | |
handleClose(); | |
} | |
}; | |
var handleOpen = function(opener, message) { | |
var content = document.createElement('div'); | |
content.setAttribute('role', 'document'); | |
content.setAttribute('tabindex', '0'); | |
var labelId = dialog.getAttribute('aria-labelledby'); | |
var label = document.createElement('p'); | |
label.textContent = message; | |
label.setAttribute('id', labelId); | |
var button = document.createElement('button'); | |
// this might be redundant | |
button.setAttribute('role', 'button'); | |
// this is a safeguard against embedding in a form we don't want to submit | |
button.setAttribute('type', 'button'); | |
button.textContent = 'Close'; | |
button.addEventListener('click', handleClose); | |
button.addEventListener('keydown', handleRefocus); | |
content.appendChild(label); | |
content.appendChild(button); | |
dialog.appendChild(content); | |
dialog.opener = opener; | |
dialog.setAttribute('open', 'true'); | |
dialog.setAttribute('aria-hidden', 'false'); | |
content.focus(); | |
// let parent listen to the ESC key | |
dialog.parentNode.addEventListener('keydown', handleEscape); | |
}; | |
var handleRefocus = function(e) { | |
var k = e.which || e.keyCode; | |
// refocus on the dialog label if user tries to tab out | |
if (k == '9') { | |
dialog.querySelector('[tabindex="0"]').focus(); | |
e.preventDefault(); | |
} | |
}; | |
document.addEventListener('DOMContentLoaded', function() { | |
var buttons = [].slice.call(document.querySelectorAll('[data-dialog-message]')); | |
buttons.map(function(button) { | |
button.addEventListener('click', handleClick); | |
}); | |
}); | |
}()); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment