HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
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.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
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.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
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.
Using packages here is powered by esm.sh, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ESM usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<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:
<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:
<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 & height for a:
<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> Doesn't pause video on exit. Investigating… 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 & 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 (< 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 (< 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]]]
/* 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;
}
// 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));
Also see: Tab Triggers