Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <main id="content">

  <h1><a href="http://websemantics.uk/articles/accessible-modal-dialog-popup-iframe/" target=_blank>Accessible modal dialog pop-up iframe (v1)</a></h1>

  <nav>
    <a href="https://codepen.io/2kool2/pen/KzXYmV" title="[new window]" target=_blank>Version 1</a>
    <a href="https://codepen.io/2kool2/pen/YqePqQ" title="[new window]" target=_blank>Version 2</a>
    <a href="https://codepen.io/2kool2/pen/dXazyK" title="[new window]" target=_blank>Version 3</a>
    <a href="https://codepen.io/2kool2/pen/LkaXay" title="[new window]" target=_blank>Version 4</a>
  </nav>

  <p><a href="https://codepen.io/2kool2/pen/LkaXay" title="[new window]" target=_blank>Version 4</a> - updated and is production ready.</p>

  <p>Button (map image) click activates a modal window, which contains a mapping iframe.</p>
  <p>All keyboard control is sent to the modal. On close, focus returns to the activating button.</p>
  <p>Live example: <a href="http://www.tesco.com/carrier-bags/#where">Tesco carrier bags</a> (rubbish)</p>

  <!-- The activator -->
  <button class=btn id="btn_map_open" data-iframesrc="https://www.google.com/maps/d/embed?mid=zA9_X1Tfn01s.kNe-p7G1WCjk">
      <img id=img_map class="img-map" src="http://www.tesco.com/carrier-bags/i/map.png" alt="View interactive map in a modal window.">
    </button>



  <div>
    <h2>Based on the workings of:</h2>
    <ul>
      <li>Greg Kraus: <a href="https://accessibility.oit.ncsu.edu/training/aria/modal-window/version-3/">The incredible accessible modal window v3</a></li>
      <li>Marco Zehe: <a href="https://www.marcozehe.de/2015/02/05/advanced-aria-tip-2-accessible-modal-dialogs/">Accessible modal dialogs</a></li>
      <li>Heydonworks: <a href="http://heydonworks.com/practical_aria_examples/#warning-dialog">Warning dialog</a></li>
    </ul>
    
    <p>Caveat: A button is purposefully used here because Tesco servers (running Omniture) hijacks links for analytics and adds its own behaviour.</p>
<p><a href="https://codepen.io/2kool2/pen/dXazyK" title="[new window]" target=_blank>Version 3</a> uses anchor links instead.</p>

    
    <h2>On page load</h2>
    <p>A <code>div</code> is created, to act as a modal window, and appended to the body tag. A lightbox <code>div</code> is also added. The <code>iframe</code> is not populated until the activating button is pressed.</p>
    
    <h2>On button activation</h2>
    
    <p>The #content is marked with <code>aria-hidden="true"</code> preventing screen-reader interaction.</p>
    <p>An overlay, #lightbox, is placed over the #content reducing visibility to indicate non-active state. Note: Clicking the lightbox closes the modal window returning focus to the button.</p>
    <p>The modal window, #modal_map, is first made visible, then its <code>iframe</code> content fetched and the focus caret is moved to the modal heading.</p>
    
    <h2>While modal dialog is displayed</h2>
    
    <p>Keyboard access is restricted to only interact within the modal dialog. The tab key loops through all of the keyboard focusable items.</p>
    <p>The escape key is mapped to close the modal window, though cross-origin policies may prevent <key>Esc</key> from working while the focus caret is inside an <code>iframe</code>.</p>
    <p>The <code>h1</code> title of the modal dialog is identified through the <code>aria-labelledby</code> attribute.</p>
    <p>Off-screen instructions, presented via the <code>aria-describedby</code> attribute, describe the modal dialog and how to interact with it.</p>
    <p>The last focusable object in the modal dialog is the close button which, once clicked, closes the #modal_map and #lightbox, returning focus to the initiating button.</p>
    
    <h2>To do:</h2>
    <p>See <a href="https://codepen.io/2kool2/pen/dXazyK" title="[new window]" target=_blank>version 3</a>.</p>
  </div>
</main>



<!-- Footer codepen include -->
[[[https://codepen.io/2kool2/pen/mKeeGM]]]
              
            
!

CSS

              
                .m2c {margin:2rem auto}

/* Popup modal */

.modal-map {
  background-color: #fff;
  position: absolute;
  left: 10%;
  right: 10%;
  top: 5%;
  bottom: 5%;
  z-index: 10;
  border: 1px solid #3a3a3a;
  border-radius: .25rem;
  box-shadow: 0 .25rem .5rem rgba(0, 0, 0, .25);
  opacity: 0;
  transition: opacity 0.8s ease-out;
}

.modal-map[aria-hidden="false"] {
  position: fixed;
  opacity: 1;
}


/* Light box properties */

.lightbox {
  display: none;
  text-indent: -200em;
  background-color: rgba(0, 0, 0, 0.8);
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  /* places the modal overlay between the main page and the modal dialog*/
  z-index: 5;
}

.lightbox.ON {
  display: block;
}

.modal-title,
.modal-desc {
  margin: 0;
  position: absolute;
  top: 5px;
  left: -200rem;
  background-color: #fff;
  color: #000;
  text-shadow: 0 0 0 #fff;
  font-size: 1.5rem;
}

.modal-title:focus,
.modal-desc:focus {
  left: 5px;
}


/* The pop-ups close button, moved via CSS to the top right of the pop-up */

.btn-modalClose {
  position: absolute;
  top: -20px;
  right: -20px;
  border-radius: 50%;
  width: 40px;
  height: 40px;
  background-color: #000;
  box-shadow: 0 .25rem .5rem rgba(0, 0, 0, .25);
  overflow: hidden;
}

.btn-modalClose:hover,
.btn-modalClose:active,
.btn-modalClose:focus {
  background-color: #c00;
}

.btn-modalClose span {
  position: absolute;
  top: 0;
  left: -200em;
  display: block;
}
.btn-modalClose svg {
  fill: #fff;
  vertical-align: middle;
  margin: 8px;
}
/* The pop-up content div will scroll if it has too much content */

.btn {
  border: 0 solid;
  margin: 2rem auto;
  display:block;
  transition: outline .3s ease-out;
}
.btn:hover,
.btn:focus {
  outline: 2px solid #fff;
}

.modal {
  display: none;
}

.img-map {
  display: block;
  width: 100%;
}

[role="dialog"][aria-hidden="true"] {
  display: none;
}

[role="dialog"][aria-hidden="false"] {
  display: block;
}


/* modal loading graphic - http://loading.io/ */

.modal-map {
  background-image: url("data:image/svg+xml;charset=utf8,<svg width='128' height='128' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid' class='uil-gears'><g transform='translate(-20,-20)'><path d='M79.9 52.6C80 51.8 80 50.9 80 50s0-1.8-0.1-2.6l-5.1-0.4c-0.3-2.4-0.9-4.6-1.8-6.7l4.2-2.9c-0.7-1.6-1.6-3.1-2.6-4.5L70 35c-1.4-1.9-3.1-3.5-4.9-4.9l2.2-4.6c-1.4-1-2.9-1.9-4.5-2.6L59.8 27c-2.1-0.9-4.4-1.5-6.7-1.8l-0.4-5.1C51.8 20 50.9 20 50 20s-1.8 0-2.6 0.1l-0.4 5.1c-2.4 0.3-4.6 0.9-6.7 1.8l-2.9-4.1c-1.6 0.7-3.1 1.6-4.5 2.6l2.1 4.6c-1.9 1.4-3.5 3.1-5 4.9l-4.5-2.1c-1 1.4-1.9 2.9-2.6 4.5l4.1 2.9c-0.9 2.1-1.5 4.4-1.8 6.8l-5 0.4C20 48.2 20 49.1 20 50s0 1.8 0.1 2.6l5 0.4c0.3 2.4 0.9 4.7 1.8 6.8l-4.1 2.9c0.7 1.6 1.6 3.1 2.6 4.5l4.5-2.1c1.4 1.9 3.1 3.5 5 4.9l-2.1 4.6c1.4 1 2.9 1.9 4.5 2.6l2.9-4.1c2.1 0.9 4.4 1.5 6.7 1.8l0.4 5.1C48.2 80 49.1 80 50 80s1.8 0 2.6-0.1l0.4-5.1c2.3-0.3 4.6-0.9 6.7-1.8l2.9 4.2c1.6-0.7 3.1-1.6 4.5-2.6L65 69.9c1.9-1.4 3.5-3 4.9-4.9l4.6 2.2c1-1.4 1.9-2.9 2.6-4.5L73 59.8c0.9-2.1 1.5-4.4 1.8-6.7L79.9 52.6zM50 65c-8.3 0-15-6.7-15-15 0-8.3 6.7-15 15-15s15 6.7 15 15C65 58.3 58.3 65 50 65z' fill='%239F9DB2'><animateTransform attributeName='transform' type='rotate' from='90 50 50' to='0 50 50' dur='1s' repeatCount='indefinite'/></path></g><g transform='translate(20,20) rotate(15 50 50)'><path d='M79.9 52.6C80 51.8 80 50.9 80 50s0-1.8-0.1-2.6l-5.1-0.4c-0.3-2.4-0.9-4.6-1.8-6.7l4.2-2.9c-0.7-1.6-1.6-3.1-2.6-4.5L70 35c-1.4-1.9-3.1-3.5-4.9-4.9l2.2-4.6c-1.4-1-2.9-1.9-4.5-2.6L59.8 27c-2.1-0.9-4.4-1.5-6.7-1.8l-0.4-5.1C51.8 20 50.9 20 50 20s-1.8 0-2.6 0.1l-0.4 5.1c-2.4 0.3-4.6 0.9-6.7 1.8l-2.9-4.1c-1.6 0.7-3.1 1.6-4.5 2.6l2.1 4.6c-1.9 1.4-3.5 3.1-5 4.9l-4.5-2.1c-1 1.4-1.9 2.9-2.6 4.5l4.1 2.9c-0.9 2.1-1.5 4.4-1.8 6.8l-5 0.4C20 48.2 20 49.1 20 50s0 1.8 0.1 2.6l5 0.4c0.3 2.4 0.9 4.7 1.8 6.8l-4.1 2.9c0.7 1.6 1.6 3.1 2.6 4.5l4.5-2.1c1.4 1.9 3.1 3.5 5 4.9l-2.1 4.6c1.4 1 2.9 1.9 4.5 2.6l2.9-4.1c2.1 0.9 4.4 1.5 6.7 1.8l0.4 5.1C48.2 80 49.1 80 50 80s1.8 0 2.6-0.1l0.4-5.1c2.3-0.3 4.6-0.9 6.7-1.8l2.9 4.2c1.6-0.7 3.1-1.6 4.5-2.6L65 69.9c1.9-1.4 3.5-3 4.9-4.9l4.6 2.2c1-1.4 1.9-2.9 2.6-4.5L73 59.8c0.9-2.1 1.5-4.4 1.8-6.7L79.9 52.6zM50 65c-8.3 0-15-6.7-15-15 0-8.3 6.7-15 15-15s15 6.7 15 15C65 58.3 58.3 65 50 65z' fill='%23C5D2E0'><animateTransform attributeName='transform' type='rotate' from='0 50 50' to='90 50 50' dur='1s' repeatCount='indefinite'/></path></g></svg>");
  background-repeat: no-repeat;
  background-position: center center;
}
              
            
!

JS

              
                (function() {
  
  "use strict";
  
  // Modal window iframe (map) version 1.0 05-04-2016

  // Assumption:
  //    First object in modal is the modal title
  //    Last object is the modal close button
  // Therefore to keep tab control inside modal:
  //    listen for tab keypress on close button
  //    listen for shift-tab keypress on title
  // Always listen for the ESC key.
  
  // To do: 
  //    Window resize.
  //    Externalise hard coded attribute values.
  //    Allow multiple instances.

  var content = document.getElementById("content");
  var btn_map_open = document.getElementById("btn_map_open");
  var modalDiv;
  var lightboxDiv;
  var modal_title;
  var btn_modal_close;
  var iframeSrc;

  var _closeModal = function() {
    var modal_map = document.getElementById("modal_map");
    var lightbox = document.getElementById("lightbox");
    if (modal_map) {
      modal_map.setAttribute("aria-hidden", "true");
      lightbox.className = lightbox.className.replace(" ON", "");
      content.setAttribute("aria-hidden", "false");
      // move focus back to initialising button
      btn_map_open.focus();
    }
  };

  var _createiframe = function(image, modalHeight) {
    var iframe = document.createElement("iframe");
    if (image) {
      iframe.id = "mapFrame";
      iframe.src = image.getAttribute("data-iframesrc");
      // iframe.src = image.src;
      iframe.width = image.offsetWidth;
      iframe.height = modalHeight;
      iframe.setAttribute("frameborder", 0);
      iframe.setAttribute("allowfullscreen", true);
    }
    return iframe;
  };

  var _modal_title_keypressed = function(e) {
    // if tab key and shift
    if (e.which === 9 && e.shiftKey) {
      e.preventDefault();
      //focus on last object in modal
      btn_modal_close.focus();
    }
  };

  var _keypressed = function(e) {
    // only if ESC pressed
    if (e.which === 27) {
      _closeModal();
    }
  };

  var _displayModal = function () {
    var modal_map = document.getElementById("modal_map");
    var image = document.getElementById("img2iframe");
    var modalHeight = modal_map.offsetHeight;

    if (image) {
      if (modalHeight > image.offsetHeight) {
        modalHeight = image.offsetHeight;
      }
      modal_map.style.maxHeight = modalHeight + "px";
      modal_map.replaceChild(_createiframe(image, modalHeight), image);
      // ESC key check
      modal_map.addEventListener("keydown", _keypressed, false);
    }
  };

  var _openModal = function(e) {

    e.preventDefault();

    var modal_map = document.getElementById("modal_map");
    var lightbox = document.getElementById("lightbox");

    if (modal_map && lightbox) {
      if (!lightbox.className.match(" ON")) {
        lightbox.className += " ON";
      }
      modal_map.setAttribute("aria-hidden", "false");
      window.requestAnimationFrame(_displayModal);
      content.setAttribute("aria-hidden", "true");
      modal_title = document.getElementById("modal_title");
      if (modal_title) {
        modal_title.addEventListener("keydown", _modal_title_keypressed, false);
        // move focus to the modal h1
        modal_title.focus();
      }
    }
  };

  var _getModalHTML = function() {
    var str;
    var img_map = document.getElementById("img_map");
    str = "<h1 tabindex=0 id=modal_title class=modal-title>Tesco Bags of Help projects interactive map</h1>";
    str += "<div id=modal_desc class=modal-desc>Esc key to leave, tab &amp; shift-tab to move focus.</div>";
    str += "<img id=img2iframe data-iframesrc=\"";
    str += iframeSrc;
    str += "\" class=img-map src=\"" + img_map.src + "\" alt=\"Interactive map\">";
    str += "<button id=btn_modal_close class=\"btn btn-modalClose\"><span>Close</span>";
    // SVG not checked in IE
    str += '<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path d="M311.1,250.2L487.2,74c16.9-16.9,16.9-44.2,0-61.1C470.3-3.9,443-3.9,426.1,13L250,189.1L73.9,13C57-3.9,29.7-3.9,12.8,13s-16.8,44.2,0,61l176.1,176.1L12.8,426.3c-16.9,16.9-16.9,44.2,0,61.1c8.4,8.4,19.5,12.6,30.5,12.6s22.1-4.2,30.5-12.6L250,311.2l176.1,176.1c8.4,8.4,19.5,12.6,30.5,12.6s22.1-4.2,30.5-12.6c16.9-16.9,16.9-44.2,0-61.1L311.1,250.2z"></path></svg>';
    str += "</button>";
    return str;
  };

  var _addModalBlock = function() {
    modalDiv = document.createElement("div");
    modalDiv.id = "modal_map";
    modalDiv.className = "modal-map"
    modalDiv.setAttribute("aria-hidden", "true");
    modalDiv.setAttribute("aria-labelledby", "modal_title");
    modalDiv.setAttribute("aria-describedby", "modal_desc");
    modalDiv.setAttribute("role", "dialog");
    modalDiv.innerHTML = _getModalHTML();
    document.body.appendChild(modalDiv);
  };

  var _btn_modal_close_keypressed = function(e) {
    // if tab key and not shift
    if (e.which === 9 && !e.shiftKey) {
      e.preventDefault();
      //focus on first object in modal
      modal_title.focus();
    }
  };

  var _addModalCloseBtn = function() {
    btn_modal_close = document.getElementById("btn_modal_close");
    if (btn_modal_close) {
      btn_modal_close.addEventListener("click", _closeModal, false);
      btn_modal_close.addEventListener("keydown", _btn_modal_close_keypressed, false);
    }
  };

  var _addLightbox = function() {
    lightboxDiv = document.createElement("div");
    lightboxDiv.id = "lightbox";
    lightboxDiv.className = "lightbox";
    // optional - should not be able to reach this via keyboard
    lightboxDiv.setAttribute("tabindex", "0");
    document.body.appendChild(lightboxDiv);
    // mouse / touch only
    lightboxDiv.addEventListener("click", _closeModal, false);
  };

  if (content && btn_map_open) {
    iframeSrc = btn_map_open.getAttribute("data-iframesrc");
    if (iframeSrc) {
      _addModalBlock();
      _addModalCloseBtn();
      _addLightbox();
      btn_map_open.addEventListener("click", _openModal, false);
    }
  }

})();
              
            
!
999px

Console