Modal Dialog

  • To open dialog, click the "Display Dialog" button
  • Once in the modal, for keyboard users, the focus is retained within the modal until modal is dismissed (circular tab order)
  • Clicking the close button, clicking the cancel button, pressing the ESCAPE key, OR clicking (with mouse only) anywhere outside of the modal will dismiss the modal dialog

HTML Source Code (button)

<div id="container">
  <button type="button" id="alert-trigger">Display Dialog</button>
</div>

HTML Source Code (dialog)

This code should be placed at the very bottom of the document, so that you can toggle aria-hidden"true" on the main content of the page without affecting the dialog.

<div id="continue-dialog" role="dialog" aria-labelledby="admessagehead1" aria-describedby="admessagebody1">
  <button type="button" id="continue-close" aria-label="close" >
    <i class="fa fa-times"></i>
  </button>
  <h2 id="admessagehead1">Sign Up</h2>
  	<div id="dialog-inner-wrapper">
      <div id="admessagebody1" tabindex="-1">
      	Enter your contact information
      </div>
      <div>
      	<label for="first-name">First Name:</label>
      </div>
      <div class="error" id="first-name-error">
        	Please provide your first name
      </div>
      <div>
      	<input type="text" id="first-name" aria-required="true">
      </div>
      <div>
        <label for="last-name">Last Name:</label>
      </div>
      <div class="error" id="last-name-error">
        	Please provide your last name
      </div>
      <div>
        <input type="text" id="last-name" aria-required="true">
      </div>
      <div>
        <label for="email-address">Email Address:</label>
        </div>
	  <div class="error" id="email-address-error">
        	Please provide your email address
      </div>
      <div>
        <input type="text" id="email-address" aria-required="true">
      </div>
      <div id="form-actions">
        <button type="button" id="submit">Submit</button>
        <button type="button" id="cancel">Cancel</button>
      </div>
	</div>
</div>
<div id="success-cage"></div>

JavaScript Source Code

Dependencies:
  • JQuery
var successMarkup = '<div id="success-bin" aria-label="Success" role="alert" tabindex="-1">' +
                      '<div id="success-message">' +
                        'Thank you for providing your contact information!!' +
                      '</div>' +
                    '</div>';
var $trigger = $('#alert-trigger');
var $modal = $('#continue-dialog');
var $cancel = $('#cancel');
var $submit = $('#submit');
var $close = $('#continue-close');
var $first = $('#first-name');
var $last = $('#last-name');
var $email = $('#email-address');
var $container = $('#fullContainer');

// when the trigger is clicked, show the modal and focus it
$trigger.on('click', function (e) {
  e.stopPropagation();
  $('#success-cage').html('');
  $container.attr('aria-hidden', 'true');
  $('body').prepend('<div id="temporaryOverlay"></div>');
  $modal.show();
  setTimeout(function () {
    // $modal[0].focus();
    $('#admessagebody1')[0].focus();
  }, 0);
});

// when cancel is clicked, hide the modal and focus it's trigger
$cancel.on('click', function () {
  $('#conf-error').hide();
  $('#success-bin').hide();
  $container.removeAttr('aria-hidden');
  $('#temporaryOverlay').remove();
  $modal.hide();
  $trigger.focus();
});

$close.on('click', function () {
  $cancel.click();
});

// keyboard events on the modal itself
$modal.on('keydown', function (e) {
  var which = e.which;
  var target = e.target;

  if (which === 9 && e.shiftKey) { // SHIFT + TAB
    // shift+tab from "X" (close) button to the "Cancel" button
    if (target === $close[0] || target === $modal[0]) {
      e.preventDefault();
      $cancel.focus();
    }
  } else if (which === 9) { // TAB
    // tab from "Cancel" button to "X" (close) button
    if (target === $cancel[0]) {
      e.preventDefault();
      $close.focus();
    }
  } else if (which === 27) { // ESCAPE
    // click the cancel button which hides the modal and focuses it's trigger
    $cancel.click();
  }
});

// clicks on the modal's "Submit" button
$submit.on('click', function () {
  var erroneousInputs = [];
  // validation:
  $([$first[0], $last[0], $email[0]]).each(function () {
    // initial clean up:
    $(this).removeAttr('aria-invalid').removeAttr('aria-describedby');
    $('#' + this.id + '-error').hide();

    if (!this.value) {
      // applies aria-invalid="true" and associates the input
      // with it's error message via `aria-describedby`
      erroneousInputs.push(this);
      $(this).attr({
        'aria-invalid': 'true',
        'aria-describedby': this.id + '-error'
      });

      // display the error message
      $('#' + this.id + '-error').show();
    }
  });

  if (!erroneousInputs.length) { // NO ERRORS!
    $modal.hide();
    $container.removeAttr('aria-hidden');
    $('#success-cage').html(successMarkup);
    $('#success-bin').focus();
  } else {
    // focus the first erroneous input
    erroneousInputs[0].focus();
  }
});


// clicking outside of the modal while the modal is
// open will close the dialog and focus the trigger
$(document).on('click', function (e) {
  if ($modal.is(':visible') && !$(e.target).closest('#continue-dialog')[0]) {
    $container.removeAttr('aria-hidden');
	$('#temporaryOverlay').remove();
    $modal.hide();
    $trigger.focus();
  }
});

CSS Source Code

body { 
	margin:0;
	padding:0;
	}

#continue-dialog {
  display: none;
  position: absolute;
  min-width: 200px;
  margin: 0 auto;
  text-align: center;
  top: 70px;
  z-index: 2000;
  background-color: #fff;
  border: 2px solid  #666;
  border-radius:5px;
  position:fixed;
	width: 25%;
	min-width: 300px;
	left: 37%;
  margin:auto;
  top:20%;
  line-height:1.5em;
}

#continue-dialog h2 {
  margin: 0;
  padding: 5px;
  background-color: #eee;
  border-radius: 5px 5px 0 0;
}

#admessagebody1 {
	padding:5px;
	display:inline-block;
}

#dialog-inner-wrapper {
	padding:5px;

}

input[type=text] {
	min-width:200px;
}

#success-bin {
  font-size: 28px;
  padding: 10px;
  text-align: center;
  border: 2px solid green;
}

#continue-close {
  position: absolute;
  top: 3px;
  right: 3px;
}

#form-actions {
	padding:10px 0;	
}

.error {
  display: none;
  color: #666;
  font-size: 15px;
  font-style: italic;
  text-align: center;
}

#temporaryOverlay {
	position:absolute;
	z-index:100;
	background-color:#000;
	opacity:0.2;
	width:100%;
	height:100%;
}

Copy and Paste Full Page Example