Difference between revisions of "Modal dialog with ARIA"

From Level Access Web Labs
Jump to navigation Jump to search
(Add aria-describedby on the close modal dialog button)
 
(30 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
==Example==
 
==Example==
 +
 +
Notes:
 +
 +
* Not implemented:
 +
** Constraining keyboard focus within the modal dialog
 +
 +
* Known issues:
 +
** VoiceOver has issues with role="dialog" (and aria-modal="true") on iOS 10 and 11, but not on iOS 9. Specifically, it doesn't read all of the dialog content, and activating the check box jumps focus back to the heading of the dialog.
 +
 
<html>
 
<html>
 
<head>
 
<head>
 
   <style type="text/css">
 
   <style type="text/css">
 
     .sr-only {
 
     .sr-only {
 +
      position: absolute;
 +
      clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
 +
      clip: rect(1px, 1px, 1px, 1px);
 +
      clip-path: inset(50%);
 +
      padding: 0;
 +
      border: 0;
 +
      height: 1px;
 +
      width: 1px;
 +
      overflow: hidden;
 +
      white-space: nowrap;
 
     }
 
     }
  
 
     .dialog-hide {
 
     .dialog-hide {
 
       display:none;
 
       display:none;
 +
    }
 +
 +
    .dialog-title {
 +
      background-color:black;
 +
      color:white;
 +
      margin-top:0em;
 +
      font-weight:bold;
 +
    }
 +
 +
    .modal-shading-overlay {
 +
      width:100%;
 +
      height:100%;
 +
      z-index:2; /* places the modal overlay between the main page and the modal dialog*/
 +
      background-color:#000;
 +
      opacity:0.5;
 +
      position:fixed;
 +
      top:0;
 +
      left:0;
 +
      display:none;
 +
      margin:0;
 +
      padding:0;
 
     }
 
     }
  
 
     .dialog-show {
 
     .dialog-show {
 +
      width:50%;
 +
      margin-left:auto;
 +
      margin-right:auto;
 +
      padding: 5px;
 +
      border: thin #000 solid;
 +
      background-color:#fff;
 +
      z-index:3; /* places the modal dialog on top of everything else */
 +
      position:fixed;
 +
      top:25%;
 +
      left:25%;
 
       display:block;
 
       display:block;
      border: solid thin black;
 
      width:30em;
 
      text-align:center;
 
    }
 
 
    .body-dimmed {
 
 
     }
 
     }
 
   </style>
 
   </style>
  
 
   <script type="text/javascript">
 
   <script type="text/javascript">
    function openDialog1() {
 
      document.getElementById("dialog1").className = "dialog-show";
 
  
      setTimeout(function(){
+
    var focusStack = [];
        document.getElementById("dialog1").focus();
 
        document.getElementById("pageContent").setAttribute("aria-hidden", "true");
 
        document.getElementById("pageContent").className = "body-dimmed";
 
      },2000); // 2 sec timeout just for debugging purposes
 
    }
 
  
    function closeDialog1() {
+
    function openDialog1() {
      document.getElementById("pageContent").setAttribute("aria-hidden", "false");
+
      // Note that we don't use aria-hidden on the dialog, since the
      document.getElementById("pageContent").className = "";
+
      //  dialog is _actually_ hidden by CSS (and HTML5)
 +
      document.getElementById("dialog1").className = "dialog-show";
 +
      // Best way to hide content: "display:none" and the hidden attribute
 +
      document.getElementById("dialog1").removeAttribute("hidden");
  
      setTimeout(function(){
+
      setTimeout(function(){
        document.getElementById("openDialog1").focus();
+
        focusStack.push(document.activeElement);
        document.getElementById("dialog1").className = "dialog-hide";
+
        <!-- document.getElementById("dialog1").focus(); -->
      },2000); // 2 sec timeout just for debugging purposes
+
        document.getElementById("closeDialog2").focus();
    }
+
        document.getElementById("modalShadingOverlay").style.display = "block";
 +
      //  document.getElementById("pageContent").className = "body-dimmed";
 +
        document.getElementById("pageContent").setAttribute("aria-hidden", "true");
 +
      },1);
 +
    }
 +
 
 +
    function closeDialog1() {
 +
      document.getElementById("pageContent").setAttribute("aria-hidden", "false");
 +
      document.getElementById("modalShadingOverlay").style.display = "none";
 +
    //  document.getElementById("pageContent").className = "";
 +
 
 +
      setTimeout(function(){
 +
        var toFocus = focusStack.pop();
 +
 
 +
        try {
 +
          if ( toFocus ) {
 +
            toFocus.focus();
 +
          } else {
 +
              document.body.focus();
 +
          }
 +
        }
 +
        catch (e) {
 +
        }
 +
        document.getElementById("dialog1").className = "dialog-hide";
 +
        // Best way to hide content: "display:none" and the hidden attribute
 +
        document.getElementById("dialog1").setAttribute("hidden", "hidden");
 +
      },1);
 +
    }
 
   </script>
 
   </script>
  
Line 48: Line 115:
 
   <div id="pageContent" aria-hidden="false">
 
   <div id="pageContent" aria-hidden="false">
  
     ...
+
     </br>...</br>
  
 
     <!-- A control for selecting your favorite kind of tree -->
 
     <!-- A control for selecting your favorite kind of tree -->
Line 54: Line 121:
  
 
     <!-- A button to get more info about trees -->
 
     <!-- A button to get more info about trees -->
     <button id="openDialog1" onclick="openDialog1();">More info<span class="sr-only"> about trees </span></button>
+
     <button type="button" id="openDialog1" onclick="openDialog1();">More info<span class="sr-only"> about trees - opens modal dialog</span></button>
  
 
     <select id="favoriteTree">
 
     <select id="favoriteTree">
Line 63: Line 130:
 
     </select>
 
     </select>
  
     ...
+
     </br>...</br>
  
 
   </div>
 
   </div>
  
   <div role="dialog" tabindex="-1" id="dialog1" aria-labelledby="dialog1Title" aria-describedby="dialog1Desc" class="dialog-hidden">
+
   <!-- <div role="region" tabindex="-1" id="dialog1" aria-labelledby="dialog1Title" aria-describedby="dialog1Desc" class="dialog-hide" hidden> -->
     <!-- Mark the end of the dialog for browsers/AT which don't support ARIA -->
+
  <!-- <div role="dialog" tabindex="-1" id="dialog1" aria-labelledby="dialog1Title" aria-describedby="dialog1Desc" aria-modal="true" class="dialog-hide" hidden> -->
     <span aria-hidden="true" class="sr-only"> Beginning of modal dialog </span>
+
  <div role="dialog" tabindex="-1" id="dialog1" aria-labelledby="dialog1Title" aria-describedby="dialog1Desc" class="dialog-hide" hidden>
 +
     <!-- Mark the beginning of the dialog
 +
        NOTE: If the Javascript applies "aria-hidden" to ALL of the rest of the page content,
 +
              the following line SHOULD add the attribute aria-hidden="true" (or may be
 +
              removed completely, unless browser/AT combinations which don't support
 +
              ARIA must be supported)
 +
    -->
 +
     <span class="sr-only" aria-hidden="true"> Beginning of modal dialog </span>
  
     <h1 id="dialog1Title">What you need to know about trees</h1>
+
    <button type="button" id="closeDialog2" onclick="closeDialog1();" aria-describedby="dialog1Title">Close<span class="sr-only"> modal dialog </span></button>
 +
     <h1 id="dialog1Title" class="dialog-title">What you need to know about trees</h1>
  
 
     <div id="dialog1Desc">
 
     <div id="dialog1Desc">
  
       <p>Trees are great! They convert CO2 into oxygen, so we can breathe!</p>
+
       <p>Trees are great! They convert CO2 into oxygen, without which we wouldn't be able to breathe!</p>
 +
 
 +
    </div>
 +
 
 +
    <!-- Note that the following content is not referenced by aria-labelledby or aria-describedby
 +
        for the dialog, but it must be accessible for screen reader users -->
 +
 
 +
    <h2>Pine tree</h2>
 +
    <p>Description of pine trees ...</p>
 +
 
 +
    <h2>Oak tree</h2>
 +
    <p>Description of oak trees ...</p>
 +
 
 +
    <h2>Redwood tree</h2>
 +
    <p>Description of redwood trees ...</p>
 +
 
 +
    <h2>Aspen tree</h2>
 +
    <p>Description of aspen trees ...</p>
  
      <ul>
+
    <hr>
        <li>
 
          <h2>Pine tree</h2>
 
          <p>Description of pine trees ...</p>
 
        </li>
 
        <li>
 
          <h2>Oak tree</h2>
 
          <p>Description of oak trees ...</p>
 
        </li>
 
        <li>
 
          <h2>Redwood tree</h2>
 
          <p>Description of redwood trees ...</p>
 
        </li>
 
        <li>
 
          <h2>Aspen tree</h2>
 
          <p>Description of aspen trees ...</p>
 
        </li>
 
      </ul>
 
  
      <label for="i1">Was this helpful? </label><input id="i1" type="checkbox" />
+
    <label for="i1">Was this helpful? <input id="i1" type="checkbox" /></label>
  
     </div>
+
     <hr>
  
 
     <button type="button" id="closeDialog1" onclick="closeDialog1();">Close<span class="sr-only"> modal dialog </span></button>
 
     <button type="button" id="closeDialog1" onclick="closeDialog1();">Close<span class="sr-only"> modal dialog </span></button>
  
     <!-- Mark the end of the dialog for browsers/AT which don't support ARIA -->
+
     <!-- Mark the end of the dialog
     <span aria-hidden="true" class="sr-only"> End of modal dialog </span>
+
        NOTE: If the Javascript applies "aria-hidden" to ALL of the rest of the page content,
 +
              the following line SHOULD add the attribute aria-hidden="true" (or may be
 +
              removed completely, unless browser/AT combinations which don't support
 +
              ARIA must be supported)
 +
    -->
 +
     <span class="sr-only" aria-hidden="true"> End of modal dialog </span>
 
   </div>
 
   </div>
 +
 +
  <div tabindex="-1" id="modalShadingOverlay" class="modal-shading-overlay"></div>
 
</body>
 
</body>
 
</html>
 
</html>

Latest revision as of 04:50, 3 July 2018

Example

Notes:

  • Not implemented:
    • Constraining keyboard focus within the modal dialog
  • Known issues:
    • VoiceOver has issues with role="dialog" (and aria-modal="true") on iOS 10 and 11, but not on iOS 9. Specifically, it doesn't read all of the dialog content, and activating the check box jumps focus back to the heading of the dialog.


...

...