Created
February 23, 2012 22:43
-
-
Save kloots/1895502 to your computer and use it in GitHub Desktop.
ARIA Widget Examples Using jQuery
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 http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="author" content="Todd Kloots"> | |
<title>Registration Form</title> | |
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.4.1/build/cssreset/reset-min.css"> | |
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.4.1/build/cssfonts/fonts-min.css"> | |
<style type="text/css"> | |
h1 { | |
margin: 1em; | |
font-weight: bold; | |
} | |
#personal-info { | |
margin: 1em; | |
padding: 1em; | |
border: solid 1px #000; | |
} | |
#personal-info label { | |
width: 10em; | |
display: inline-block; | |
text-align: right; | |
} | |
#errors { | |
color: #B70000; | |
font-weight: bold; | |
} | |
.error { | |
margin-left: 10.5em; | |
color: #B70000; | |
font-weight: bold; | |
} | |
.invalid { | |
outline: solid 1px #B70000; | |
} | |
#personal-info input[type=text], | |
#personal-info textarea { | |
border: solid 1px #999; | |
width: 15em; | |
} | |
#personal-info legend { | |
font-weight: bold; | |
} | |
#personal-info legend span { | |
display: block; | |
padding: 0 .25em; | |
} | |
#personal-info div { | |
margin: .25em 0; | |
} | |
#personal-info textarea { | |
height: 3em; | |
} | |
#personal-info fieldset { | |
background-color: #ccc; | |
padding: .25em; | |
border: solid 1px #000; | |
} | |
#personal-info #gender, | |
#personal-info #birthday { | |
border: 0; | |
} | |
#gender legend, | |
#birthday legend { | |
margin: 0 0 0 -.25em; | |
font-weight: normal; | |
} | |
#gender legend span, | |
#birthday legend span{ | |
width: 10em; | |
display: inline-block; | |
text-align: right; | |
} | |
#gender label, | |
#birthday label { | |
display: inline; | |
width: auto; | |
} | |
#gender div, | |
#birthday div { | |
margin: -1.5em 0 0 10.25em; | |
} | |
#birthday div label { | |
position: absolute; | |
left: -999em; | |
} | |
#personal-info #birth-year { | |
width: 4em; | |
} | |
#preferences label { | |
display: inline; | |
} | |
#preferences span { | |
width: 10em; | |
display: inline-block; | |
text-align: right; | |
} | |
#preferences label#interests-lbl { | |
width: 10em; | |
display: inline-block; | |
vertical-align: top; | |
} | |
#preferences legend span { | |
width: auto; | |
text-align: left; | |
} | |
.dialog { | |
position: absolute; | |
-moz-box-shadow: 3px 3px 3px rgba(0,0,0,0.2), -3px 3px 3px rgba(0,0,0,0.2); | |
-webkit-box-shadow: 3px 3px 3px rgba(0,0,0,0.2), -3px 3px 3px rgba(0,0,0,0.2); | |
box-shadow: 3px 3px 3px rgba(0,0,0,0.2), -3px 3px 3px rgba(0,0,0,0.2); | |
} | |
.dialog fieldset { | |
border: solid 1px #000; | |
background-color: #fff; | |
} | |
.dialog legend { | |
display: block; | |
width: 100%; | |
} | |
.dialog legend span { | |
background-color: #ccc; | |
display: block; | |
border: solid 1px #000; | |
margin: 0 -1px; | |
font-weight: bold; | |
padding: 3px 6px; | |
} | |
.dialog .bd { | |
padding: 6px; | |
} | |
.dialog .ft { | |
padding: 6px; | |
} | |
.dialog.hidden { | |
display: none; | |
} | |
.modal-visible { | |
overflow: hidden; | |
} | |
.modal-mask { | |
position: absolute; | |
overflow: hidden; | |
background-color: #000; | |
opacity: .5; | |
filter: alpha(opacity=50); | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
z-index: 0; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="reg-form" aria-hidden="false"> | |
<h1>Registration Form</h1> | |
<form id="personal-info" live="polite" relevant="additions"> | |
<fieldset> | |
<legend class="title"><span>Personal Information</span></legend> | |
<div><label for="first-name">First Name</label> <input type="text" name="first-name" id="first-name" aria-required="true" required></div> | |
<div><label for="last-name">Last Name</label> <input type="text" name="last-name" id="last-name" aria-required="true" required></div> | |
<fieldset id="gender"> | |
<legend><span>Gender</span></legend> | |
<div> | |
<label for="gender-male">Male</label> <input type="radio" name="gender" id="gender-male" checked> | |
<label for="gender-female">Female</label> <input type="radio" name="gender" id="gender-female"> | |
</div> | |
</fieldset> | |
<fieldset id="birthday"> | |
<legend><span>Birthday</span></legend> | |
<div> | |
<label for="birth-month">Month</label> | |
<select id="birth-month" name="birth-month"> | |
<option value="1">January</option> | |
<option value="2">February</option> | |
<option value="3">March</option> | |
<option value="4">April</option> | |
<option value="5">May</option> | |
<option value="6">June</option> | |
<option value="7">July</option> | |
<option value="8">August</option> | |
<option value="9">September</option> | |
<option value="10">October</option> | |
<option value="11">November</option> | |
<option value="12">December</option> | |
</select> | |
<label for="birth-day">Day</label> | |
<select id="birth-day" name="birth-day"> | |
<option value="1">1</option> | |
<option value="2">2</option> | |
<option value="3">3</option> | |
<option value="4">4</option> | |
<option value="5">5</option> | |
<option value="6">6</option> | |
<option value="7">7</option> | |
<option value="8">8</option> | |
<option value="9">9</option> | |
<option value="10">10</option> | |
<option value="11">11</option> | |
<option value="12">12</option> | |
<option value="13">13</option> | |
<option value="14">14</option> | |
<option value="15">15</option> | |
<option value="16">16</option> | |
<option value="17">17</option> | |
<option value="18">18</option> | |
<option value="19">19</option> | |
<option value="20">20</option> | |
<option value="21">21</option> | |
<option value="22">22</option> | |
<option value="23">23</option> | |
<option value="24">24</option> | |
<option value="25">25</option> | |
<option value="26">26</option> | |
<option value="27">27</option> | |
<option value="28">28</option> | |
<option value="29">29</option> | |
<option value="30">30</option> | |
<option value="31">31</option> | |
</select> | |
<label for="birth-year">Year</label> | |
<input type="text" id="birth-year" name="birth-year"> | |
</div> | |
</fieldset> | |
</fieldset> | |
<fieldset class="section"> | |
<legend><span>Home</span></legend> | |
<div><label for="home-address">Address</label> <textarea id="home-address" name="home-address"></textarea></div> | |
<div><label for="home-city">City</label> <input type="text" id="home-city" name="home-city"></div> | |
<div><label for="home-state">State</label> <input type="text" id="home-state" name="home-state"></div> | |
<div><label for="postal-code">Postal Code</label> <input type="text" id="postal-code" name="postal-code"></div> | |
</fieldset> | |
<fieldset class="section"> | |
<legend><span>Contact Information</span></legend> | |
<div><label for="email">Email</label> <input type="email" id="email" name="email" aria-required="true" required></div> | |
<div><label for="phone">Phone</label> <input type="tel" id="phone" name="phone"></div> | |
</fieldset> | |
<div id="toolbar"> | |
<input type="button" id="signup" value="Submit"> | |
<input type="button" id="cancel" value="Cancel"> | |
</div> | |
</form> | |
</div> | |
<div class="dialog hidden" role="alertdialog" aria-labelledby="dialog-label" aria-describedby="msg"> | |
<form> | |
<fieldset> | |
<legend class="hd"><span id="dialog-label">Confirm Action</span></legend> | |
<div class="bd" id="msg"> | |
<p>Are you sure you want to submit this form?</p> | |
</div> | |
<div class="ft"> | |
<input type="button" id="ok-button" value="OK"> | |
<input type="button" id="cancel-button" value="Cancel"> | |
</div> | |
</fieldset> | |
</form> | |
</div> | |
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> | |
<script type="text/javascript"> | |
(function ($) { | |
function createModalMask() { | |
var doc = document, | |
docEl = $.browser.msie && doc.compatMode ? doc.documentElement : doc.body; | |
$(docEl).addClass("modal-visible"); | |
$('<div class="modal-mask"></div>').insertBefore('.dialog'); | |
sizeModalMask(); | |
$("#reg-form").attr("aria-hidden", true); | |
} | |
function removeModalMask() { | |
$('.modal-mask').remove(); | |
$("#reg-form").attr("aria-hidden", false); | |
} | |
function sizeModalMask() { | |
var modalMask = $('.modal-mask'), | |
win = $(win); | |
modalMask.css({ | |
width: win.width(), | |
height: win.height() | |
}); | |
} | |
function positionDialog() { | |
var win = $(window); | |
dialog.css({ | |
top: ((win.height()/2) - (dialog.height()/2)), | |
left: ((win.width()/2) - (dialog.width()/2)) | |
}); | |
} | |
function enforceModality(e) { | |
if (!$.contains(dialog[0], e.target)) { | |
dialog.find('#ok-button').focus(); | |
} | |
} | |
var dialog = $(".dialog"); | |
function hideDialog(activeEl) { | |
dialog.addClass("hidden"); | |
removeModalMask(); | |
dialog.off(".dialog"); | |
$(window).off(".dialog"); | |
activeEl.focus(); | |
} | |
function showDialog() { | |
var activeEl = document.activeElement; | |
dialog.removeClass("hidden"); | |
createModalMask(); | |
positionDialog(); | |
$('#ok-button').focus(); | |
$(document).on("focusin.dialog", enforceModality); | |
$(window).on('resize.dialog', function () { | |
sizeModalMask(); | |
positionDialog(); | |
}); | |
dialog.on("click.dialog", "input", activeEl, hideDialog); | |
dialog.on("keydown.dialog", function (e) { | |
var keyCode = e.keyCode; | |
if (keyCode === 27) { // Esc | |
hideDialog(activeEl); | |
} | |
// Handle Tab out from "Cancel" button | |
// This is necessary because the dialog is the last element in the DOM, making the | |
// "Cancel" button the last focusable element in the DOM. When the user presses Tab | |
// focus goes to the browser chrome, but we don't get a focus event. Further, | |
// when focus moves to the browser chrome, Firefox doesn't clear document.activeElement | |
// meaning you cannot detech that the the dialog has lost focus and direct focus back to it. | |
else if (keyCode === 9 && !e.shiftKey && e.target === $("#cancel-button")[0]) { | |
e.preventDefault(); | |
} | |
}); | |
} | |
function showErrors(fields) { | |
fields = $(fields); | |
fields.each(function () { | |
var field = $(this), | |
invalid = field.hasClass("invalid"), | |
descID = this.id + "-desc"; | |
if (field.val().length === 0 && !invalid) { | |
field.addClass("invalid").attr({ | |
"aria-invalid": true, | |
"aria-describedby": descID | |
}).after('<p class="error" id="' + descID + '">' + $("label[for=" + this.id + "]").text() + ' cannot be blank.</p>'); | |
} | |
}); | |
if ($(".error").length > 0 && !$("#errors")[0]) { | |
$("#personal-info").prepend('<div id="errors" role="alert">This form has one or more errors</div>'); | |
} | |
} | |
function clearErrors(fields) { | |
fields = $(fields); | |
fields.each(function () { | |
var field = $(this); | |
if (field.val().length > 0) { | |
field.removeClass("invalid").attr("aria-invalid", false); | |
field.parent().find(".error").remove(); | |
} | |
}); | |
var errorSummary = $("#errors"); | |
if ($(".error").length === 0 && errorSummary[0]) { | |
errorSummary.remove(); | |
} | |
} | |
var regForm = $("#personal-info"); | |
regForm.on("blur", "input[required]", function () { | |
showErrors("#" + this.id); | |
}); | |
regForm.on("blur", "input.invalid", function () { | |
clearErrors("#" + this.id); | |
}); | |
$("#signup").on("click", function () { | |
showErrors("input[required]"); | |
if ($(".error").length === 0) { | |
showDialog(); | |
} | |
else { | |
$("input.invalid")[0].focus(); | |
} | |
}); | |
}(jQuery.noConflict())); | |
</script> | |
</body> | |
</html> |
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 http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="author" content="Todd Kloots"> | |
<title>ListBox Test Page</title> | |
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.4.1/build/cssfonts/cssfonts-min.css"> | |
<style type="text/css"> | |
ul { | |
list-style-type: none; | |
margin: 2px 0 0 0; | |
padding: 0; | |
border: 2px inset #ccc; | |
width: 176px; | |
} | |
li.selected { | |
background: #ccc; | |
} | |
li:focus { | |
outline: none; | |
} | |
ul.focus { | |
border-color: #3875D7; | |
} | |
ul.focus li.selected { | |
background: #3875D7; | |
color: #fff; | |
} | |
</style> | |
</head> | |
<body> | |
<ul id="listbox" role="listbox" aria-label="Interests"> | |
<li role="option" tabindex="0">Activism</li> | |
<li role="option" tabindex="-1">Baking</li> | |
<li role="option" tabindex="-1">Cooking</li> | |
<li role="option" tabindex="-1">Dancing</li> | |
<li role="option" tabindex="-1">Fine Art</li> | |
<li role="option" tabindex="-1">Ice Skating</li> | |
<li role="option" tabindex="-1">Music</li> | |
<li role="option" tabindex="-1">Politics</li> | |
<li role="option" tabindex="-1">Sports</li> | |
<li role="option" tabindex="-1">Travel</li> | |
<li role="option" tabindex="-1">Technology</li> | |
</ul> | |
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> | |
<script type="text/javascript"> | |
(function ($) { | |
var listbox = $("#listbox"); | |
function makeSelection(li) { | |
listbox.find("li.selected").attr({ | |
'tabIndex': -1, | |
'aria-selected': false | |
}).removeClass("selected"); | |
li.attr({ | |
'tabIndex': 0, | |
'aria-selected': true | |
}).addClass("selected"); | |
} | |
listbox.on("mousedown", "li", function () { | |
makeSelection($(this)); | |
}); | |
listbox.on("keydown", "li", function (e) { | |
var keyCode = e.keyCode, | |
li = $(this), | |
next, | |
nextItem; | |
if (keyCode === 38) { // UP | |
next = "prev"; | |
} | |
else if (keyCode === 40) { // DOWN | |
next = "next"; | |
} | |
if (next) { | |
// On initial arrow key press, don't advance focus, | |
// just synchronize focus and selection | |
if (!li.hasClass('selected')) { | |
li.addClass("selected").attr('aria-selected', true); | |
} | |
else if ((nextItem = li[next]()) && nextItem[0]) { | |
makeSelection(nextItem); | |
nextItem.focus(); | |
} | |
} | |
}); | |
listbox.on("focusin", function (e) { | |
if (!listbox.hasClass("focus")) { | |
listbox.addClass("focus"); | |
} | |
}); | |
listbox.on("focusout", function (e) { | |
setTimeout(function () { | |
if (!$.contains(listbox[0], document.activeElement)) { | |
listbox.removeClass("focus"); | |
} | |
}, 0); | |
}); | |
}(jQuery.noConflict())); | |
</script> | |
</body> | |
</html> |
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 http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="author" content="Todd Kloots"> | |
<title>Toolbar Example</title> | |
<style type="text/css"> | |
.toolbar { | |
border: 1px solid #666; | |
background: #ddd; | |
padding: 3px; | |
font-size: 0; | |
float: left; | |
} | |
.toolbar button { | |
float: left; | |
border: 1px solid #333; | |
background: #bbb; | |
padding: 0; | |
margin: 0 3px 0 0; | |
width: 36px; | |
height: 36px; | |
overflow: hidden; | |
} | |
/* Remove the extra padding and border given to buttons by | |
default in Firefox to ensure correct alignment of the img. */ | |
.toolbar button::-moz-focus-inner { | |
border: 0; | |
padding: 0; | |
} | |
/* Hide the text label by inserting an image before it. */ | |
.toolbar button:before { | |
display: inline-block; | |
content: url('assets/toolbar_icon_set_full.png'); | |
} | |
.toolbar button { | |
*background: url('assets/toolbar_icon_set_full.png') no-repeat; | |
*text-indent: 36px; | |
} | |
.toolbar button.focus, | |
.toolbar button:focus { | |
outline: none; | |
background-color: #3875D7; | |
} | |
.toolbar .prnt { | |
*background-position: -38px -74px; | |
} | |
.toolbar .prnt:before { | |
margin: -73px 0 0 -37px; | |
} | |
.toolbar .find { | |
*background-position: -182px -146px; | |
} | |
.toolbar .find:before { | |
margin: -145px 0 0 -181px; | |
} | |
.toolbar .save { | |
*background-position: -146px -74px; | |
} | |
.toolbar .save:before { | |
margin: -73px 0 0 -145px; | |
} | |
.toolbar .sets { | |
*background-position: -74px -110px; | |
} | |
.toolbar .sets:before { | |
margin: -109px 0 0 -73px; | |
} | |
.toolbar .info { | |
*background-position: -146px -146px; | |
} | |
.toolbar .info:before { | |
margin: -145px 0 0 -145px; | |
} | |
</style> | |
</head> | |
<body> | |
<div role="toolbar" class="toolbar"> | |
<button type="button" tabindex="0" class="prnt">Print</button> | |
<button type="button" tabindex="-1" class="find">Find</button> | |
<button type="button" tabindex="-1" class="save">Save</button> | |
<button type="button" tabindex="-1" class="sets">Settings</button> | |
<button type="button" tabindex="-1" class="info">Info</button> | |
</div> | |
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> | |
<script type="text/javascript"> | |
(function ($) { | |
var toolbar = $(".toolbar"), | |
buttons = toolbar.find("button"), | |
browser = $.browser; | |
buttons.attr("tabIndex", -1); | |
buttons.first().attr("tabIndex", 0); | |
if (browser.msie && browser.version < 8) { | |
toolbar.on("focus", "button", function () { | |
$(this).addClass("focus"); | |
}); | |
toolbar.on("blur", "button", function () { | |
$(this).removeClass("focus"); | |
}); | |
} | |
toolbar.on("keydown", "button", function (e) { | |
var keyCode = e.keyCode, | |
button = $(this), | |
next; | |
if (keyCode === 37) { // Left | |
next = "prev"; | |
} | |
else if (keyCode === 39) { // Right | |
next = "next"; | |
} | |
if (next) { | |
button[next]().focus(); | |
} | |
}); | |
}(jQuery.noConflict())); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment