Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Dialog Tutorial</title>
</head>
<body>
<article id="example-introduction">
  <h1>Dialog Tutorial</h1>
  <p>View the source of this page to see how a visually appealing, yet fully accessible dialog can be created. The rules for accessible dialogs are laid out in <a href="https://www.w3.org/WAI/PF/aria-practices/#dialog_modal">WAI-ARIA 1.0 Authoring Practices</a></p>
  <p>There are two input fields surrounding the &quot;open dialog&quot; button to mimic interactive content outside of the dialog. They have no other purpose than serve as proof of focus being trapped within the dialog once it&apos;s shown.</p>
</article>
<div id="example-html">
  <main>
    <div>
      <label for="before-button">Demo Text Input before Dialog Button</label> <input id="before-button">
    </div>
    <section>
      <button type="button" id="open-dialog">Show Dialog</button>
    </section>
    <label for="after-button">Demo Text Input after Dialog Button</label> <input id="after-button">
  </main>
  <!--
    If not using the <dialog> element of HTML5.1, it's best to use the <div> element for widgets
    with semantic meaning that can only be defined via aria.
    It is not uncommon to wrap the dialog's content in a <form>,
    provide the confirmation button <button type="submit">OK</button>,
    and wire up the submit event to close the dialog after processing
    the user's input (while preventing the form submission itself).
    This has the added benefit of <kbd>Enter</kbd> working out of the
    box, without JavaScript having to listen for the key to be pressed.
    Also touchscreen devices (such as modern mobile phones) have
    built-in heuristics that change the appearance of OnScreenKeyboards
    depending on <form> and <input> type.
    When dealing with elements that contain focus, it is a good idea to make them focusable themselves
    by adding tabindex="-1". This way a mouse click in the background of the dialog won't pass focus
    to the document, but retain it in the dialog.
    The aria attributes role, aria-labeledby and aria-describedby are necessary for the Accessibility Tree
    to properly expose our dialog widget. Their specific meaning is explained here:
    * https://www.w3.org/TR/wai-aria/roles#dialog
    * https://www.w3.org/TR/wai-aria/states_and_properties#aria-labelledby
    * https://www.w3.org/TR/wai-aria/states_and_properties#aria-describedby
  -->
  <div id="dialog" role="dialog" aria-labelledby="dialog-title" aria-describedby="dialog-description" tabindex="-1" hidden>
    <form class="dialog-content">
      <header>
        <!--
          The dialog's header explains what the user is being interrupted for.
          While a label is considered mandatory, an extended description is optional.
        -->
        <h1 id="dialog-title">Name Entry</h1>
        <p id="dialog-description">Please enter your full name.</p>
      </header>
      <section>
        <!--
          A dialog can do anything from displaying simple text, to hosting
          complex user interaction. But since the latter is generally
          considered bad practice, try keeping your dialogs as simple
          as possible.
        -->
        <label for="within-dialog">Name</label> <input id="within-dialog">
      </section>
      <footer>
        <!--
          the footer of a dialog usually contains its control buttons
          to close, confirm or do something else entirely.
        -->
        <button type="button" id="close-dialog">Close</button>
        <button type="submit" id="save-dialog">Save</button>
      </footer>
    </form>
  </div>
</div>
<script src="https://cdn.jsdelivr.net/ally.js/1.4.1/ally.min.js"></script>
</body>
</html>
 
/*
  For styling overlays you may want to have a look at
  http://tympanus.net/codrops/2013/11/07/css-overlay-techniques/
*/
/*
  make the dialog container spans the entire screen,
  this is necessary to capture clicks
*/
#dialog {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(0,0,0,0);
  z-index: 999;
}
/*
  obscure everything but the dialog
  unless the backdrop needs a special animation,
  this element could be ignored and the backdrop
  simply applied to the #dialog element via
  background: rgba(0, 0, 0, 0.25);
*/
#dialog:before {
  content: "";
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.25);
  z-index: 999;
  -webkit-transition: opacity 0.2s ease-in-out;
          transition: opacity 0.2s ease-in-out;
}
/* show the dialog in the center of the screen */
#dialog .dialog-content {
  box-sizing: border-box;
  /* maintain on screen even during scroll (problematic on mobile) */
  position: fixed;
  /* when centering, try using FlexBox instead of this junk */
  top: 50%;
  left: 50%;
  width: 300px;
  height: 200px;
  -webkit-transform: translate3d(-50%,-50%,0);
          transform: translate3d(-50%,-50%,0);
  /* dialogs are usually box things that are on top of everything */
  padding: 20px;
  border: 1px solid #CCC;
  background: white;
  z-index: 1000;
  -webkit-transition: transform 0.2s ease-in-out;
          transition: transform 0.2s ease-in-out;
}
/* style the dialog's contents */
#dialog h1 {
  margin: 0;
}
#dialog footer {
  margin-top: 20px;
}
/* hide the dialog in a way that allows animations */
#dialog[hidden],
#dialog[hidden]:before,
#dialog[hidden] .dialog-content {
  display: block;
  visibility: hidden;
  -webkit-transform: translate3d(0px, -1px, 0px) scale(0);
          transform: translate3d(0px, -1px, 0px) scale(0);
  -webkit-transition:
    visibility 0s linear 0.2s,
    transform 0s linear 0.2s;
  transition:
    visibility 0s linear 0.2s,
    transform 0s linear 0.2s;
}
#dialog[hidden]:before {
  opacity: 0;
  /*
    overwrite the backdrops's showing transition so that dimensions
    are maintained until the box finished its opacity transformation
  */
  -webkit-transition:
    opacity 0.2s ease-in-out,
    visibility 0s linear 0.2s,
    transform 0s linear 0.2s;
  transition:
    opacity 0.2s ease-in-out,
    visibility 0s linear 0.2s,
    transform 0s linear 0.2s;
}
#dialog[hidden] .dialog-content {
  -webkit-transform: translate3d(0px, -1px, 0px) scale(0);
          transform: translate3d(0px, -1px, 0px) scale(0);
  /*
    overwrite the dialog's showing transition so that visibility
    is maintained until the box finished its transformation
  */
  -webkit-transition:
    transform 0.2s ease-in-out,
    visibility 0s linear 0.2s;
  transition:
    transform 0.2s ease-in-out,
    visibility 0s linear 0.2s;
}
  
 
// Grab the elements we need to interact with
var openButton = document.getElementById('open-dialog');
var dialog = document.getElementById('dialog');
var closeButton = document.getElementById('close-dialog');
// Data filled by openDialog() and later used by closeDialog()
var keyHandle;
var tabHandle;
var disabledHandle;
var hiddenHandle;
var focusedElementBeforeDialogOpened;
function openDialog() {
  // Remember the focused element before we opened the dialog
  // so we can return focus to it once we close the dialog.
  focusedElementBeforeDialogOpened = document.activeElement;
  // We're using a transition to reveal the dialog,
  // so wait until the element is visible, before
  // finding the first keyboard focusable element
  // and passing focus to it, otherwise the browser
  // might scroll the document to reveal the element
  // receiving focus
  ally.when.visibleArea({
    context: dialog,
    callback: function(context) {
      // the dialog is visible on screen, so find the first
      // keyboard focusable element (giving any element with
      // autofocus attribute precendence). If the dialog does
      // not contain any keyboard focusabe elements, focus will
      // be given to the dialog itself.
      var element = ally.query.firstTabbable({
        context: context, // context === dialog
        defaultToContext: true,
      });
      element.focus();
    },
  });
  // Make sure that no element outside of the dialog
  // can be interacted with while the dialog is visible.
  // This means we don't have to handle Tab and Shift+Tab,
  // but can defer that to the browser's internal handling.
  disabledHandle = ally.maintain.disabled({
    filter: dialog,
  });
  // Make sure that no element outside of the dialog
  // is exposed via the Accessibility Tree, to prevent
  // screen readers from navigating to content it shouldn't
  // be seeing while the dialog is open. See example:
  // https://marcysutton.com/slides/mobile-a11y-seattlejs/#/36
  hiddenHandle = ally.maintain.hidden({
    filter: dialog,
  });
  // Make sure that Tab key controlled focus is trapped within
  // the tabsequence of the dialog and does not reach the
  // browser's UI, e.g. the location bar.
  tabHandle = ally.maintain.tabFocus({
    context: dialog,
  });
  // React to enter and escape keys as mandated by ARIA Practices
  keyHandle = ally.when.key({
    escape: closeDialogByKey,
    // Note: in non-interactive content you would also bind the enter
    // key to close the dialog. In our example the form's submit event
    // will cover that instead. The enter handler would be executed
    // for *every* press of the enter key, even if the focused element
    // is a button (which would invoke the default action). Try using
    // a <form> for interactive content to get around that problem.
    // enter: closeDialogByKey,
  });
  // Show the dialog
  dialog.hidden = false;
}
function closeDialogByKey() {
  // we need to let the keyboard event handlers finish,
  // before actually closing the dialog. Otherwise the
  // keydown of <kbd>enter</kbd> will close the dialog,
  // focus is passed back to the open-dialog-button and
  // the keyup of <kbd>enter</kbd> will open the dialog
  // again.
  setTimeout(closeDialog);
  // alternatively we could've called event.preventDefault()
  // and then run closeDialog() synchronously
}
function closeDialog() {
  // undo listening to keyboard
  keyHandle.disengage();
  // undo trapping Tab key focus
  tabHandle.disengage();
  // undo hiding elements outside of the dialog
  hiddenHandle.disengage();
  // undo disabling elements outside of the dialog
  disabledHandle.disengage();
  // return focus to where it was before we opened the dialog
  focusedElementBeforeDialogOpened.focus();
  // hide or remove the dialog
  dialog.hidden = true;
}
function saveDialog(event) {
  // do not submit the form
  event.preventDefault();
  // do something with the entered data
  var name = dialog.querySelector('input').value;
  console.log('entered name', name);
  closeDialog();
}
// wire up showing/hiding the dialog
openButton.addEventListener('click', openDialog, false);
closeButton.addEventListener('click', closeDialog, false);
dialog.addEventListener('submit', saveDialog, true);
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
rodneyrehmpro
0viewers