Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs 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 its URL and the proper URL extension.

+ 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

Auto Save

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 class=-modal>

  <h1><a href="https://websemantics.uk/articles/accessible-modal-dialog-popup-iframe/" target=_blank>Accessible modal dialog pop-up iframe (v4)</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>Uses an anchor to launch a modal pop-up which is then populated with an <code>iframe</code>. This version removed the requirement for an image, also allows modal title and description to be user defined.</p>
  
  <p>Production ready (encapuslated with external configuration and instantiation) version available: <a href="https://websemantics.uk/articles/accessible-modal-dialog-popup-iframe/demo/" title="[new window]" target=_blank>Modal Dialog demo</a>. For minified JavaScript see source.</p>


  <p class=u-margin2>Test as link: &nbsp;
    <a 
       class="lnk_modal-open" 
       data-modalTitle=""  
       data-modalDesc="" 
       href="https://www.google.com/maps/place/147+Wardour+St,+Soho,+London+W1F+8WD,+UK/@51.514197,-0.134724,16z/data=!4m5!3m4!1s0x487604d357825039:0xf0c170d8fa918a9b!8m2!3d51.5141967!4d-0.1347244?hl=en-GB" 
       data-modal="https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d4175.8218601402605!2d-0.1390235133502716!3d51.51397674271494!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x487604d357825039%3A0xf0c170d8fa918a9b!2s147+Wardour+St%2C+Soho%2C+London+W1F+8WD%2C+UK!5e0!3m2!1sen!2sin!4v1468326690641">
      Wine bar location (interactive map)
    </a>
  </p>
  
  <p class=u-margin2>Test as button: &nbsp;
    <button 
            data-modal="https://www.google.com/maps/d/embed?mid=zA9_X1Tfn01s.kNe-p7G1WCjk">
      Carrier bag (interactive map)
    </button>
  </p>

  <p>Test as link image:
    <a 
       class="lnk_modal-open lnk_modal-img"
       href="https://www.google.com/maps/place/147+Wardour+St,+Soho,+London+W1F+8WD,+UK/@51.514197,-0.134724,16z/data=!4m5!3m4!1s0x487604d357825039:0xf0c170d8fa918a9b!8m2!3d51.5141967!4d-0.1347244?hl=en-GB" 
       data-modal="https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d4175.8218601402605!2d-0.1390235133502716!3d51.51397674271494!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x487604d357825039%3A0xf0c170d8fa918a9b!2s147+Wardour+St%2C+Soho%2C+London+W1F+8WD%2C+UK!5e0!3m2!1sen!2sin!4v1468326690641">
      <img class="img_modal-open" src="https://websemantics.uk/portfolio/2016/finest-wine-bar/i/wine-bar-map-location.32.compressed.png" alt="Tesco finest* wine bar location">
    </a>
  </p>

  <p>Test as just image:
      <img 
           class="XXXimg_modal-open" 
           src="https://websemantics.uk/portfolio/2016/finest-wine-bar/i/wine-bar-map-location.32.compressed.png" 
           alt="Tesco finest* wine bar location"
           data-modal="https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d4175.8218601402605!2d-0.1390235133502716!3d51.51397674271494!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x487604d357825039%3A0xf0c170d8fa918a9b!2s147+Wardour+St%2C+Soho%2C+London+W1F+8WD%2C+UK!5e0!3m2!1sen!2sin!4v1468326690641">
  </p>


  <h2>In use</h2>
  <ul>
    <li>Add attribute <code>data-modal</code> to any link (best practice), or button, or tag (not recommended) to indicate "Launch in modal".</li>
    <li>Give <code>data-modal</code> the url of the <code>iframe</code> <code>src</code> if it's different to the link <code>href</code>.</li>
    <li>The modal <code>title</code> may be taken from the optional data attribute <code>data-title=""</code>, or the link text, or the image <code>alt</code> text.</li>
    <li>The modal description may also be overriden by the data attribute <code>data-desc</code>.</li>
  </ul>


  <p class=u-margin2>
    <a 
       class="lnk_modal-open" 
       href="https://websemantics.uk/articles/accessible-modal-dialog-popup-iframe/" 
       data-modal>
      webSemantics article (web page)
    </a>
  </p>


  <p>Please note this modal neither raises or lowers accessibility barriers caused by the <code>iframe</code> content. Be certain the <code>iframe</code> doesn't trap the keyboard!</p>


  <h2>Features</h2>
  <ul>
    <li><abbr title="Web Content Accessibility Guidelines">WCAG</abbr> 2 level AA with <abbr title="Accessible Rich Internet Applications">ARIA</abbr> support</li>
    <li>Fully responsive, mobile-first approach.</li>
    <li>Lightweight, less than 1.8 KB to the browser with 1.5 KB CSS.</li>
    <li>When opening; the focus is moved to modal content.</li>
    <li>When closing; the focus is moved back to the activating link.</li>
    <li>While open; the <kbd>Tab</kbd> scope is limited to the modal content.</li>
    <li>While open; the document body is frozen (no scroll, no keychain).</li>
    <li>While open; the <kbd>Esc</kbd> key closes the modal while on any focusable object (but not while inside the <code>iframe</code>).</li>
    <li>All customisations are via inline HTML attributes and CSS.<br>No need to edit JavaScript.</li>
    <li>Just a link is presented when JavaScript is unavailable.</li>
  </ul>


  <h2>Current status</h2>
  <p>14/08/2016 - In cross-browser, cross-platform and cross-device testing.<br>To be followed with full access testing by <a href="http://www.digitalaccessibilitycentre.org/">Digital Accessibility Centre</a>.</p>


  <p> Currently resolving modal width &amp; height for a: &nbsp;  
    <a 
       class="lnk_modal-open" 
       href="https://pbs.twimg.com/profile_banners/752911976876179456/1468343984/1500x500" 
       data-modal 
       data-modalSize="(1920, 1080)">
      Pop-up image
    </a>
  </p>


  <p class=u-margin2>
    <a 
       class="lnk_modal-open" 
       href="https://www.youtube.com/embed/lOUjUirKzM0" 
       data-modal 
       data-modalSize="(1920, 1080)">
      Pop-up YouTube video
    </a> &nbsp;Doesn't pause video on exit. Investigating&hellip; Need this soonest. May just remove the iframe on exit.
  </p>


  <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>

  <h2>Alternatives</h2>
  <p>Let's mention alternate versions of modal pop-ups out there:</p>
  <ul>

    <li><a href="http://www.humaan.com/modaal/">Modaal</a> - The best I've seen though kicks in at 17 KB. Does it all except allow for different link <code>href</code> to a modal <code>iframe</code> src (as per this example). If you need Accessibility with bells &amp; whistles this is <strong>recommended</strong>. [Nicked some of your feature docs, thanks].</li>

    <li><a href="https://robinparisi.github.io/tingle/">Tingle js</a> - Love the UX feel of this one but Accessibility not the strong point. Code weight (3 kb loaded) and a style I'm envious of. Appreciated the prototype technique over my purely functional.
      [Nicked the lightbox <code>cursor</code>, thanks].</li>

    <li><a href="https://thephuse.github.io/vanilla-modal/">Vanilla modal</a> - Loved the lightweight (&lt; 4 KB loaded inc CSS) simplicity, hated the modals loading before interaction, sadly ignores keyboard-only users too.</li>

    <li><a href="https://frend.co/components/dialogmodal/">DialogModal</a> - Very basic modal dialog but with good Accessibility. Everything preloaded loaded in the HTML and uses ES2015 though.</li>

    <li>This version 4 (&lt; 4 KB inc CSS) - <del title="Removed">Still has an assumption which I'd like to remove</del>. Uses <code>classList</code> so limited to IE10+.</li>

  </ul>

  <p>All in all nothing is ever perfect, just avoid using a heavy weight library / framework for something this simple.</p>

</main>

<svg style="display:none">
  <defs>
    <symbol viewBox="0 0 38 38" id="icon-cross">
      <path d="M19 17.6l5.3-5.4 1.4 1.4-5.3 5.4 5.3 5.3-1.4 1.4-5.3-5.3-5.3 5.3-1.4-1.4 5.2-5.3-5.2-5.3 1.4-1.4 5.3 5.3z"/>
    </symbol>
    <symbol viewBox='0 0 150 130' id="icon-loading">
      <title>Loading</title>
      <path d='M81.5 33l30.8-32.8c0.3-0.3 0.5-0.2 0.3 0.3 -1.8 5.2-1.7 15.3-1.7 15.3 -0.1 6.8-0.8 11.7-6.6 17.9L74.8 65.1c-0.2 0.2-0.4 0-0.3-0.2 1.5-5.1 1.2-15.1 1.2-15.1C75.4 45.6 76.4 38.4 81.5 33M105.9 54.8l43.8 10.3c0.4 0.1 0.4 0.4-0.2 0.4 -5.4 1-14.1 6.1-14.1 6.1 -6 3.3-10.5 5.2-18.8 3.2l-41.9-9.9c-0.3-0.1-0.2-0.3 0-0.4 5.2-1.3 13.7-6.5 13.7-6.5C92 55.9 98.7 53.1 105.9 54.8M99.4 86.3l13 43.2c0.1 0.4-0.1 0.5-0.4 0.1 -3.6-4.2-12.4-9.2-12.4-9.2 -5.8-3.5-9.7-6.5-12.2-14.6L75 64.5c-0.1-0.3 0.2-0.4 0.3-0.2 3.7 3.9 12.5 8.6 12.5 8.6C91.5 74.8 97.3 79.2 99.4 86.3M68.7 97l-30.8 32.8c-0.3 0.3-0.5 0.2-0.3-0.3 1.8-5.2 1.7-15.3 1.7-15.3 0.1-6.8 0.8-11.7 6.6-17.9l29.5-31.4c0.2-0.2 0.4 0 0.3 0.2 -1.5 5.1-1.2 15.1-1.2 15.1C74.8 84.4 73.8 91.6 68.7 97M44.1 75.8L0.3 65.4C-0.1 65.3-0.1 65 0.5 65c5.4-1 14.1-6.1 14.1-6.1 6-3.3 10.5-5.2 18.8-3.2l41.9 9.9c0.3 0.1 0.2 0.3 0 0.4 -5.2 1.3-13.7 6.5-13.7 6.5C58.1 74.7 51.3 77.5 44.1 75.8M50.2 43.8l-13-43.2c-0.1-0.4 0.1-0.5 0.4-0.1C41.2 4.7 50 9.7 50 9.7c5.8 3.5 9.7 6.5 12.2 14.6l12.4 41.3c0.1 0.3-0.2 0.4-0.3 0.2 -3.7-3.9-12.5-8.6-12.5-8.6C58.1 55.4 52.4 50.9 50.2 43.8'/>
    </symbol>
  </defs>
</svg>



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

CSS

              
                
/* Modal opening object (link, button, or pretend button) */

button,
[aria-role="button"] {
  cursor: pointer;
}
button[data-modal][aria-controls],
[aria-role="button"][data-modal][aria-controls] {
  transition: all .3s ease-out;
}

/* Hover & focus indication. */

/* Reads as: if button, or aria-role=button, has data-model attribute and JavaScript has added aria-controls then on hover or focus */ 

button[data-modal][aria-controls]:hover,
button[data-modal][aria-controls]:focus,
[aria-role="button"][data-modal][aria-controls]:hover,
[aria-role="button"][data-modal][aria-controls]:focus {
  -webkit-filter: contrast(120%);
  filter: contrast(120%);
  box-shadow: 0 0 0 4px rgba(255, 0, 0, .6); /* Red so you can tell */
  outline: 0 solid;
}


/* Modal opening link cosmetics */

.lnk_modal-open {
  font-size: larger;
  background-color: #000;
  padding:.25rem .5rem;
  display: inline-block;
  text-decoration: none;
  border: 0 solid;
  margin: 0 auto;
}

.lnk_modal-img {
  padding: 0;
}

.lnk_modal-open:active {
  -webkit-filter: brightness(85%);
  filter: brightness(85%);
}

.img_modal-open {
  display: block;
  border: 0 solid;
}


/* The modal section is added via JS */

.modal {
  max-width: 80vw;
  max-height: 90vh;
  background-color: #F7F0E8;
  margin: 0 auto;
  position: absolute;
  left: 10%;
  right: 10%;
  top: 5%;
  bottom: 5%;
  z-index: 10;
  border: 1px solid #000;
  box-shadow: 0 .25em .5em #000;
  transition: opacity .5s ease-out, visibility 0s ease-out 1s, transform .5s ease-out .5s;
  backface-visibility: hidden;
  opacity: 0;
  visibility: hidden;
  transform: scale(.8) translate3d(0,0,0);
  pointer-events: none;
}

.modal[aria-hidden="false"] {
  position: fixed;
  transition-delay: 0s,0s, 0s;
  opacity: 1;
  visibility: visible;
  transform: scale(1) translate3d(0,0,0);
  pointer-events: auto;
}




/* Light box properties */

.modal_lightbox {
  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 (0) and the modal dialog (10) */
  z-index: 5;
  cursor: pointer;
  transition: opacity .5s ease-out, visibility 0s ease-out .5s;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.modal_lightbox-on {
  transition-delay: 0s, 0s;
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}
.modal_lightbox-on:hover {
  /* Stolen from trickle.js. Under consideration. SVGs will not work here */
  cursor: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAANpJREFUOBGNkz0KAjEQhYPYWXgCKws7LcXWyt7WG4i9jegBvIJ4AvEyXsRCsBDiN0sGkiE/O/A2O9n3viS7rPPeb9ETrVzPwrtHDzR1XF5I6oPWLQaeq5hDXQRw0I6xCuF5HH7Tz7oFuTkhrSyEhza8THaLoQhphpWUg/QOFyA/AFpy5nTbGrIjxvg4AiiGBzYc+rGZH9KPzFy+ZbX4bX9l+VDZr5NQMMbhbtvMxccpQ3JhpTchtXATQviItIpvW0CY7HHm8c9UDRd2chbABt3RQk2tEe8O3dDkD4JQ4iOR7BMpAAAAAElFTkSuQmCC"), pointer;
}


/* Modal title and description */

.modal_title,
.modal_desc {
  position: absolute;
  top: 5px;
  left: -200em;
  background-color: #fff;
  color: #000;
  text-shadow: 0 0 0 #fff;
  font-size: 20px;
  padding: 0.125em .25em;
  /* Tesco requirement
  font-family: Tesco_W_Rg, sans-serif; */
  margin: 0;
}

.modal_title:focus,
.modal_desc:focus {
  left: 5px;
}

[aria-hidden="false"] .modal_title {
  transition: opacity .5s ease-out 3s;
  opacity: 0;
}

.modal_title,
.modal_title:focus {
  opacity: 1;
  transition: opacity .5s ease-out;
}


/* The iframe */

.modal_iframe {
  transition: opacity .5s ease-out, visibility 0s ease-out 1s;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.modal_iframe-on {
  transition: opacity .8s ease-out, visibility 0s ease-out 0s;
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

[aria-hidden="true"] .modal_iframe {
  display: none;
}

[aria-hidden="false"] .modal_iframe {
  display: block;
}


/* The modal pop-ups close button, appears last in the modal, but is moved visually to the top right of the pop-up */

.modal_lnk-close {
  cursor: pointer;
  position: absolute;
  top: -20px;
  right: -20px;
  border: 0 solid;
  border-radius: 50%;
  width: 40px;
  height: 40px;
  background-color: #000;
  box-shadow: 0 .25em .5em rgba(0, 0, 0, .25);
  overflow: hidden;
  transition: background-color .3s ease-out;
}

.modal_lnk-close:hover,
.modal_lnk-close:active,
.modal_lnk-close:focus {
  background-color: #c00;
  outline: 0 solid;
}

.svg-close {
  pointer-events: none;
  width: 40px;
  height: 40px;
  stroke: #fff;
  stroke-width: 2;
}




/* Modal SVG (Tesco) loading animation version 2 (overlaid on itself and out of phase) */

[class*="svg-loading"] {
  position: absolute;
  width: 80px;
  height: 80px;
  top: calc(50% - 40px);
  left: calc(50% - 40px);
  z-index: -1;
  transition: opacity .3s ease-out;
  backface-visibility: hidden;
}

.svg-loading {
  fill: #00539f;
  -webkit-animation: rotate 4s linear 0s infinite;
  animation: rotate 4s linear 0s infinite;
}

.svg-loading2 {
  /* Match to .modal background colour for full effect */
  fill: #F7F0E8;
  -webkit-animation: rotate 5s linear 1s infinite;
  animation: rotate 5s linear 1s infinite;
}

@-webkit-keyframes rotate {
  to {
    -webkit-transform: rotate(360deg) translate3d(0,0,0);
    transform: rotate(360deg) translate3d(0,0,0);
  }
}

@keyframes rotate {
  to {
    -webkit-transform: rotate(360deg) translate3d(0,0,0);
    transform: rotate(360deg) translate3d(0,0,0);
  }
}



/* While modal is open */

/* Class added to body tag to prevent scroll
   Note the body does not require class "-modal" */
body.-modal-open {
  overflow: hidden;
}


/* Any tags classed with "-modal", when open, get "-modal-open" added */
/* Both of these are equivalent, best practice to use the attribute version which enforces accessibility */

.-modal[aria-hidden="true"] {
  -webkit-filter: blur(4px);
  filter: blur(4px);
}
.-modal.-modal-open {
  -webkit-filter: blur(4px);
  filter: blur(4px);
}


/* Generic helper style */
.u-margin2 {
  margin: 2rem 0;
}
              
            
!

JS

              
                
// https://john-dugan.com/javascript-debounce/
var debounce=function(e,t,n){var a;return function(){var r=this,i=arguments,o=function(){a=null,n||e.apply(r,i)},s=n&&!a;clearTimeout(a),a=setTimeout(o,t||200),s&&e.apply(r,i)}};


(function (window, d, debounce) {

  "use strict";

  // Modal pop-up window iframe version 4.1 22-08-2016

  // v4.1 - Launch from an anchor link, button or any other not recommended object
  // modal title text set by data-modalTitle, image alt or link text.
  // Object with .-modal (modalName) with have .-modal-open appended === "-" + modalName + contentClass

  // To do - under consideration:
  // make lightbox the modal section and code around it?
  // perhaps <section class=modal = lightbox
  //            <div =modal_inner = modal?
  // lightbox doesnt need to be in keychain just add onclick to close

  // To do:
  //    Option to set an ideal pop-up size eg an image
  //      - maintain the aspect ratio
  //      - centre it

  // Requires:
  //    SVG definitions for: #icon-cross, #icon-loading
  //    External functions: debounce()

  // Assumptions:
  //    First object in modal is the modal title
  //    Last object is the modal close link

  
  // defaults
  var modalName = "modal";
  var lightboxClass = "lightbox";
  var openClass = "-" + modalName + "-open";
  //var modalDesc = "<kbd>tab</kbd> or <kbd>shift + tab</kbd> to move focus.";
  var modalDesc = "Tab or Shift + Tab to move focus.";


  var _setContentObjs = function (isModalOpen) {
    var objs = d.getElementsByClassName("-" +modalName);
    var i = objs.length;
    while (i--) {
      if (!!isModalOpen) {
        objs[i].classList.add(openClass);
        if (objs[i].tagName.toLowerCase !== "body") {
          objs[i].setAttribute("aria-hidden", "true");
        }
      } else {
        objs[i].classList.remove(openClass);
        objs[i].removeAttribute("aria-hidden");
      }
    }
    return !!isModalOpen;
  };


  var _closeModal = function (e) {
    var count = e.target.count; // = lightbox, modal (ESC key), close btn
    var modalSection = d.getElementById(modalName + "_" + count);
    var lightbox = d.getElementById(modalName + "_" + count + "_" + lightboxClass);
    var modalLink;
    if (modalSection) {
      modalSection.setAttribute("aria-hidden", "true");
      lightbox.className = lightbox.className.replace(lightboxClass + "-on", "");

      _setContentObjs(!modalSection.getAttribute("aria-hidden"));
      modalLink = d.getElementById(modalSection.returnId);
      d.body.classList.remove(openClass);
      modalLink.focus();
    }
  };


  var _getModalSize = function (modalSection) {
    var clone = modalSection.cloneNode(true);
    var size = {};
    clone.className = modalName;
    clone.setAttribute("style", "position:fixed;visibility:hidden;transform: none");
    modalSection.parentElement.appendChild(clone);
    size.width = clone.clientWidth; // more performant than getBoundingClientRect
    size.height = clone.clientHeight; // more performant than getBoundingClientRect
    modalSection.parentElement.removeChild(clone);
    return size;
  };


  var _resizeIframes = function () {

    var size;
    var iframes;
    var ii;

    var modals = d.getElementsByClassName(modalName);
    var i = modals.length;

    while (i--) {

      size = _getModalSize(modals[i]);

      iframes = modals[i].getElementsByClassName(modalName + "_iframe");
      ii = iframes.length;

      while (ii--) {
        iframes[ii].width = size.width;
        iframes[ii].height = size.height;
      }
    }
  };


  var _addIframe = function (modalSection) {

    var size;
    var close_lnk;
    var frames = modalSection.getElementsByClassName(modalName + "_iframe");
    var iframe;
    if (!frames[0]) {

      iframe = d.createElement("iframe");

      // Don't display iframe until it's content is ready
      iframe.addEventListener("load", function () {
        iframe.classList.add(modalName + "_iframe-on");
      }, false);

      iframe.src = modalSection.modalSrc;
      iframe.className = modalName + "_iframe";

      size = _getModalSize(modalSection);
      iframe.width = size.width;
      iframe.height = size.height;

      iframe.setAttribute("frameborder", 0);
      iframe.setAttribute("allowfullscreen", true);

      // Add iframe before the close button
      close_lnk = d.getElementById(modalName + "_" + modalSection.count + "_lnk_close");
      modalSection.insertBefore(iframe, close_lnk);

    }
  };
  
  
  var _getTarget = function (obj) {
    var target = obj;
    var isBodyTag = obj.tagName.toLowerCase() === "body";
    if (isBodyTag) {
      return false;
    }
    if (!obj.modalSrc) {
      target = _getTarget(obj.parentElement);
    }
    return target;
  }


  var _openModal = function (e) {

    e.preventDefault();
    
    var target = _getTarget(e.target);
    
    if (target) {

      var count = target.count;
      var tempId = modalName + "_" + count;
      var tempLightboxClass = modalName + "_" + lightboxClass;
      var modalSection = d.getElementById(tempId);
      var lightbox = d.getElementById(tempId + "_" + lightboxClass);

      if (modalSection && lightbox) {
        if (!lightbox.className.match(tempLightboxClass + "-on")) {
          lightbox.className += " " + tempLightboxClass + "-on";
        }
        modalSection.setAttribute("aria-hidden", "false");
        _addIframe(modalSection);

        _setContentObjs(!!modalSection.getAttribute("aria-hidden"));

        d.body.classList.add(openClass);
        d.getElementById(modalName + "_" + count + "_title").focus();
      }
    }
  };


  var _keydown_openerObj = function (e) {
    // enter or space from the opener object
    if (e.which === 13 || e.which === 32) {
        e.preventDefault(); 
        _openModal(e);
    }
  };


  var _addOpenModalLinkAttr = function (modalLink) {
    modalLink.id = modalLink.id || "modal_" + modalLink.count + "_lnk";
    modalLink.setAttribute("aria-controls", modalName + "_" + modalLink.count);
    
    // test if it's not a button
    var tag = modalLink.tagName.toLowerCase();
    if (tag !== "button") {
      modalLink.setAttribute("aria-role", "button");
      modalLink.addEventListener("keydown", _keydown_openerObj, false);
    }
    
    // click only requires space and enter activtion too
    if (tag !== "a" || "button") {
      modalLink.tabIndex = 0;
    }
    
    modalLink.addEventListener("click", _openModal, false);
  };


  var _keydown_modal = function (e) {

    var target = e.target;

    // ESC key on anything actionable
    if (e.which === 27) {
      _closeModal(e);
    }

    // tab key and shift on the h1
    if (e.which === 9 && e.shiftKey) {
      if (target.classList.contains(modalName + "_title")) {
        e.preventDefault();
        //focus on last object in modal (close btn)
        d.getElementById(modalName + "_" + e.target.count + "_lnk_close").focus();
      }
    }

    // tab key and not shift on the close link.
    if (e.which === 9 && !e.shiftKey) {
      if (target.classList.contains(modalName + "_lnk-close")) {
        e.preventDefault(); 
        //focus on first object in modal - or should it be the modal? Requires testing
        d.getElementById(modalName + "_" + e.target.count + "_title").focus();
      }
    }

    // enter or space on the close link - why again??
    if (e.which === 13 || e.which === 32) {
      if (target.classList.contains(modalName + "_lnk-close")) {
        e.preventDefault(); 
        _closeModal(e);
      }
    }
  };


  var _getTitleText = function (modalLink) {
    var alt = "";
    var imgs = modalLink.getElementsByTagName("img");
    if (imgs && imgs[0]) {
      alt = imgs[0].alt;
    }
    return modalLink.getAttribute("data-modalTitle") || alt || modalLink.textContent; 
  };


  var _getModalTitle = function (modalLink) {
    var title = d.createElement("h1");
    title.id = modalName + "_" + modalLink.count + "_title";
    title.className = modalName + "_title";
    title.tabIndex = 0;
    title.textContent = _getTitleText(modalLink);
    title.count = modalLink.count;
    title.addEventListener("keydown", _keydown_modal, false);
    return title;
  };


  var _getModalSVG = function (icon, clss, title) {
    var svg = d.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.classList.add(clss);
    if (title) {
      var t = d.createElementNS("http://www.w3.org/2000/svg", "title");
      t.textContent = title;
      svg.appendChild(t);
    }
    var use = d.createElementNS("http://www.w3.org/2000/svg", "use");
    use.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#" + icon);
    svg.appendChild(use);
    return svg;
  };


  var _getModalDesc = function (modalLink) {
    var desc = d.createElement("p");
    desc.id = modalName + "_" + modalLink.count + "_desc";
    desc.className = modalName + "_desc";
    desc.tabIndex = 0;
    desc.innerHTML = modalLink.getAttribute("data-modalDesc") || modalDesc;
    desc.count = modalLink.count;
    desc.addEventListener("keydown", _keydown_modal, false);
    return desc;
  };


  var _getModalCloseLink = function (modalLink) {
    var link = d.createElement("a");
    link.id = modalName + "_" + modalLink.count + "_lnk_close";
    link.className = modalName + "_lnk-close";
    link.tabIndex = 0;
    link.appendChild(_getModalSVG("icon-cross", "svg-close", "Close modal"));
    link.count = modalLink.count;
    link.addEventListener("click", _closeModal, false);
    link.addEventListener("keydown", _keydown_modal, false);
    return link;
  };


  var _addModalSection = function(modalLink) {
    var section = d.createElement("section");
    section.id = modalName + "_" + modalLink.count;
    section.count = modalLink.count;
    section.returnId = modalLink.id;
    section.className = modalName;
    section.setAttribute("aria-hidden", "true");

    // should be on the activating link?
    section.setAttribute("aria-labelledby", modalName +"_" + modalLink.count + "_title");
    section.setAttribute("aria-describedby", modalName +"_" + modalLink.count + "_desc");

    section.setAttribute("role", "dialog");
    section.modalSrc = modalLink.modalSrc;

    section.appendChild(_getModalTitle(modalLink));
    section.appendChild(_getModalSVG("icon-loading", "svg-loading", "Loading"));
    section.appendChild(_getModalSVG("icon-loading", "svg-loading2", ""));
    section.appendChild(_getModalDesc(modalLink));
    section.appendChild(_getModalCloseLink(modalLink));

    d.body.appendChild(section);
  };


  var _addLightbox = function (modalLink) {

    var count = modalLink.count;
    var lightboxDiv = d.createElement("div");

    lightboxDiv.id = modalName + "_" + count + "_" + lightboxClass;
    lightboxDiv.className = modalName + "_" + lightboxClass;
    lightboxDiv.count = count;
    lightboxDiv.returnId = modalLink.id;

    // mouse / touch only
    lightboxDiv.addEventListener("click", _closeModal, false);

    d.body.appendChild(lightboxDiv);
  };


  var configuration = function (cfg) {
    modalName = cfg.modalName || modalName;
    
    lightboxClass = cfg.lightboxClass || lightboxClass;
    
    // any object with a class -modal will have the class -modal-open added when the modal is open.
    //openClass = "-" + modalName + (cfg.openClass || "-open");
    openClass = cfg.openClass ? "-" + modalName + cfg.openClass : openClass;
  };


  var initialise = function (cfg) {

    configuration(cfg);

    var modalSrc;
    var dataModals = d.querySelectorAll("[data-" + modalName + "]");

    if (dataModals) {
      var i = dataModals.length;
      while (i--) {

        // Link href and iframe src are not always the same!
        modalSrc = false;

        // use the href
        if (dataModals[i].hasAttribute("href")) {
          modalSrc = dataModals[i].href;
        }

        // overwrite src with data-modal content when available
        if (dataModals[i].getAttribute("data-modal").length) {
          modalSrc = dataModals[i].getAttribute("data-modal");
        }

        if (modalSrc) {
          dataModals[i].modalSrc = modalSrc;
          dataModals[i].count = i;
          _addOpenModalLinkAttr(dataModals[i]);
          _addModalSection(dataModals[i]);
          _addLightbox(dataModals[i]);
        }

      }

      window.addEventListener("resize", debounce(_resizeIframes, 250, false));

    }

  };

  initialise({
    modalName : "modal",  // class name of modal, also used as the base for all classes used except on SVGs.
    openClass : "-open", // is default ("-" + modaName automatically prepended)
    lightboxClass : "lightbox" // is default (modaName + "_" automatically prepended)
  });

}(window, document, debounce));
              
            
!
999px

Console