Skip to content

Commit

Permalink
Update example
Browse files Browse the repository at this point in the history
  • Loading branch information
KittyGiraudel committed Apr 21, 2018
1 parent 62f554c commit 61d6a7e
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 57 deletions.
24 changes: 11 additions & 13 deletions example/index.html
Expand Up @@ -27,23 +27,21 @@ <h1>A11y Dialog</h1>
</footer>
</div>

<div class="dialog" aria-hidden="true" id="my-accessible-dialog">
<div class="dialog" id="my-accessible-dialog">
<div class="dialog-overlay" tabindex="-1" data-a11y-dialog-hide></div>
<div class="dialog-content" aria-labelledby="dialogTitle" aria-describedby="dialogDescription" role="dialog">
<div role="document">
<button data-a11y-dialog-hide class="dialog-close" aria-label="Close this dialog window">&times;</button>
<dialog class="dialog-content" aria-labelledby="dialogTitle" aria-describedby="dialogDescription">
<button data-a11y-dialog-hide class="dialog-close" aria-label="Close this dialog window">&times;</button>

<h1 id="dialogTitle">Subscribe to ESPI Newsletter</h1>
<h1 id="dialogTitle">Subscribe to ESPI Newsletter</h1>

<p id="dialogDescription">Fill in the ridiculously small form below to receive our ridiculously cool newsletter!</p>
<p id="dialogDescription">Fill in the ridiculously small form below to receive our ridiculously cool newsletter!</p>

<form action="http://edenspiekermann.us10.list-manage.com/subscribe/post?u=927d6a7cf437da88aac7a87ec&amp;id=6823ab7bb8" method="post" target="_blank">
<label for="email">Email (required)</label>
<input type="email" name="EMAIL" id="email" placeholder="john.doe@gmail.com" required>
<button type="submit" name="button">Sign up</button>
</form>
</div>
</div>
<form action="http://edenspiekermann.us10.list-manage.com/subscribe/post?u=927d6a7cf437da88aac7a87ec&amp;id=6823ab7bb8" method="post" target="_blank">
<label for="email">Email (required)</label>
<input type="email" name="EMAIL" id="email" placeholder="john.doe@gmail.com" required>
<button type="submit" name="button">Sign up</button>
</form>
</dialog>
</div>

<script src="main.js"></script>
Expand Down
110 changes: 66 additions & 44 deletions example/main.js
@@ -1,11 +1,22 @@
/* global NodeList, Element, define */
/* global NodeList, Element, Event, define */

(function (global) {
'use strict';

var FOCUSABLE_ELEMENTS = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'iframe', 'object', 'embed', '[contenteditable]', '[tabindex]:not([tabindex^="-"])'];
var FOCUSABLE_ELEMENTS = [
'a[href]:not([tabindex^="-"]):not([inert])',
'area[href]:not([tabindex^="-"]):not([inert])',
'input:not([disabled]):not([inert])',
'select:not([disabled]):not([inert])',
'textarea:not([disabled]):not([inert])',
'button:not([disabled]):not([inert])',
'iframe:not([tabindex^="-"]):not([inert])',
'[contenteditable]:not([tabindex^="-"]):not([inert])',
'[tabindex]:not([tabindex^="-"]):not([inert])'
];
var TAB_KEY = 9;
var ESCAPE_KEY = 27;
var isDialogSupported = 'show' in document.createElement('dialog');
var focusedBeforeDialog;

/**
Expand All @@ -23,8 +34,9 @@
this._maintainFocus = this._maintainFocus.bind(this);
this._bindKeypress = this._bindKeypress.bind(this);

// Keep a reference of the node on the instance
this.node = node;
// Keep a reference of the node and the actual dialog on the instance
this.container = node;
this.dialog = node.querySelector('dialog');

// Keep an object of listener types mapped to callback functions
this._listeners = {};
Expand All @@ -41,24 +53,34 @@
*/
A11yDialog.prototype.create = function (targets) {
// Keep a collection of nodes to disable/enable when toggling the dialog
this._targets = this._targets || collect(targets) || getSiblings(this.node);
this._targets = this._targets || collect(targets) || getSiblings(this.container);

// Make sure the dialog element is disabled on load, and that the `shown`
// property is synced with its value
this.node.setAttribute('aria-hidden', true);
this.shown = false;
// Set the `shown` property to match the status from the DOM
this.shown = this.dialog.hasAttribute('open');

if (!isDialogSupported) {
this.dialog.setAttribute('role', 'dialog');

if (this.shown) {
this.container.removeAttribute('aria-hidden');
} else {
this.container.setAttribute('aria-hidden', true);
}
} else {
this.container.setAttribute('data-a11y-dialog-native', '');
}

// Keep a collection of dialog openers, each of which will be bound a click
// event listener to open the dialog
this._openers = $$('[data-a11y-dialog-show="' + this.node.id + '"]');
this._openers = $$('[data-a11y-dialog-show="' + this.container.id + '"]');
this._openers.forEach(function (opener) {
opener.addEventListener('click', this._show);
}.bind(this));

// Keep a collection of dialog closers, each of which will be bound a click
// event listener to close the dialog
this._closers = $$('[data-a11y-dialog-hide]', this.node)
.concat($$('[data-a11y-dialog-hide="' + this.node.id + '"]'));
this._closers = $$('[data-a11y-dialog-hide]', this.container)
.concat($$('[data-a11y-dialog-hide="' + this.container.id + '"]'));
this._closers.forEach(function (closer) {
closer.addEventListener('click', this._hide);
}.bind(this));
Expand All @@ -84,26 +106,25 @@
}

this.shown = true;
this.node.removeAttribute('aria-hidden');

// Iterate over the targets to disable them by setting their `aria-hidden`
// attribute to `true`; in case they already have this attribute, keep a
// reference of their original value to be able to restore it later
this._targets.forEach(function (target) {
var original = target.getAttribute('aria-hidden');

if (original) {
target.setAttribute('data-a11y-dialog-original', original);
}

target.setAttribute('aria-hidden', 'true');
});
if (isDialogSupported) {
this.dialog.showModal(event instanceof Event ? void 0 : event);
} else {
this.dialog.setAttribute('open', '');
this.container.removeAttribute('aria-hidden');

// Iterate over the targets to disable them by setting their `aria-hidden`
// attribute to `true`
this._targets.forEach(function (target) {
target.setAttribute('aria-hidden', 'true');
});
}

// Keep a reference to the currently focused element to be able to restore
// it later, then set the focus to the first focusable child of the dialog
// element
focusedBeforeDialog = document.activeElement;
setFocusToFirstItem(this.node);
setFocusToFirstItem(this.dialog);

// Bind a focus event listener to the body element to make sure the focus
// stays trapped inside the dialog while open, and start listening for some
Expand Down Expand Up @@ -132,20 +153,19 @@
}

this.shown = false;
this.node.setAttribute('aria-hidden', 'true');

// Iterate over the targets to enable them by remove their `aria-hidden`
// attribute or resetting them to their initial value
this._targets.forEach(function (target) {
var original = target.getAttribute('data-a11y-dialog-original');
if (isDialogSupported) {
this.dialog.close(event instanceof Event ? void 0 : event);
} else {
this.dialog.removeAttribute('open');
this.container.setAttribute('aria-hidden', 'true');

if (original) {
target.setAttribute('aria-hidden', original);
target.removeAttribute('data-a11y-dialog-original');
} else {
// Iterate over the targets to enable them by remove their `aria-hidden`
// attribute
this._targets.forEach(function (target) {
target.removeAttribute('aria-hidden');
}
});
});
}

// If their was a focused element before the dialog was opened, restore the
// focus back to it
Expand Down Expand Up @@ -237,7 +257,7 @@
var listeners = this._listeners[type] || [];

listeners.forEach(function (listener) {
listener(this.node, event);
listener(this.container, event);
}.bind(this));
};

Expand All @@ -259,7 +279,7 @@
// If the dialog is shown and the TAB key is being pressed, make sure the
// focus stays trapped within the dialog element
if (this.shown && event.which === TAB_KEY) {
trapTabKey(this.node, event);
trapTabKey(this.dialog, event);
}
};

Expand All @@ -273,8 +293,8 @@
A11yDialog.prototype._maintainFocus = function (event) {
// If the dialog is shown and the focus is not within the dialog element,
// move it back to its first focusable child
if (this.shown && !this.node.contains(event.target)) {
setFocusToFirstItem(this.node);
if (this.shown && !this.container.contains(event.target)) {
setFocusToFirstItem(this.dialog);
}
};

Expand Down Expand Up @@ -322,15 +342,17 @@
}

/**
* Set the focus to the first focusable child of the given element
* Set the focus to the first element with `autofocus` or the first focusable
* child of the given element
*
* @param {Element} node
*/
function setFocusToFirstItem (node) {
var focusableChildren = getFocusableChildren(node);
var focused = node.querySelector('[autofocus]') || focusableChildren[0];

if (focusableChildren.length) {
focusableChildren[0].focus();
if (focused) {
focused.focus();
}
}

Expand Down
20 changes: 20 additions & 0 deletions example/styles.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 61d6a7e

Please sign in to comment.