setupPups();
function setupPups() { /* Some links (class=pupb) in the document are trigger the display of information in a box that appears floating over the main content. This function allows the inline markup for the popups to be kept simple and generic, no need for unique identifiers). Instead, popup links and popup bodies simply need to be IN THE SAME ORDER.
Features:
• converts this markup to a popup: button
• clicking the button injects a modal window containing the popup contents
• the popup can be assigned a small/large size class in the source, or those classes will be assigned dynamically based on the word count of the popup contents
• the popup can be dismissed three ways: a close button, clicking the original again, ESC, or (coming soon?) CLICKING ANYWHERE OUTSIDE THE WINDOW
• an extra close button is placed at the bottom of very tall popup windows
• popups can have block-level contents and separated from the button in markup
Dependencies:
• no jQuery or other frameworks required, this is pure vanilla Javascript
• popup buttons and contents must be in synchronized order, e.g. buttons 1, 2, 3 must correspond to popups 1, 2, 3
Key terms:
• pup, pups = abbreviation of popup(s)
• pupb, pupbs = popup button(s)
• pupx = popup closing button
• big, sml = size classes for popup windows
*/
// get list of popup buttons, everything marked up like so: button text
var pupbs = document.getElementsByClassName('pupb');
if (pupbs.length==0) { console.log("POPUPS: looked for 'em, found none"); return; }
var pupnum = 0;
huntingBugs = false; // turn on for a bunch of extra console logging
if (huntingBugs) console.log("POPUPS: Setting up popups…\n\n");
// get list of popups, everything marked up like so:
var pups = document.getElementsByClassName('popup');
if (huntingBugs) { // log the arrays
console.log("Array of popup buttons (by button text):");
for (x = 0; x < pupbs.length; x++) console.log("popup button " + x + ": " + pupbs[x].innerHTML);
console.log("\nArray of popups (by first 20 chars of content):");
for (x = 0; x < pupbs.length; x++) console.log("popup content " + x + ": " + pups[x].innerHTML.substr(0,20));
} /**/
if (huntingBugs) console.log("\n\n=== start of popup loop\n");
var pupnum = 0;
for (x = 0; x < pupbs.length; x++) { // loop through popup buttons
pupnum++; // iterate the popup count right away, offsetting by one from the index (x+1)
var pupb = pupbs[x]; // get popup button (on the first iteration, it’s the 1st popup pair, but the 0th item in the arrays)
if (huntingBugs) console.log("making popup button #" + pupnum + " [" + pupb.innerHTML + "]");
// modify the popup button
pupb.title = "show more info in a popup"; // add some UI info
pupb.data = 'popup-' + pupnum; // attach the id of the target popup as data, to be passed as a parameter by the onclick function
pupb.onclick = function(){toglDispId(this.data);}; // add onclick fn to toggle the target popup's visibility (might seem logical to just past the ID number as a parameter direct, but it doesn’t because the value of the ID parameter will be set at the time of the event, the last time is was defined, in the final iteration of this loop, rather that using the value of ID at the time onclick listener was assigned)
// modify the target popup
if (huntingBugs) console.log("making popup #" + pupnum);
var popup = pups[x]; // capture the target popup object by its relative position
popup.id = "popup-" + pupnum; // assign an ID to the popup so it can be referenced by the buttons
popup.className += ' font_body'; // add font_body class
// popup wordcount and size classes
popup.className += assignSizeClasses(popup);
// make close button
var pupx = document.createElement('span'); // hey, new button!
pupx.className = 'pupx'; // give it some class (it’s a popup button, but it’s for closing the popup
pupx.onclick = function(){toglDisp(this.parentElement);} // give it a job (toggle popup visibility); in this case, because the button’s parent element is always the popup, we can pass the popup object directly to the toggling function
popup.appendChild(pupx); // append it to the popup
// add a second close button the bottom of big popups
if (popup.clientHeight > 600) { // if the note is tall (as rendered in pixels), add a 2nd close button for the bottom
// cleverly, this height assessment seems to play well with the previously set sml/big classes; that is, it knows the height of the popup as rendered with the applied class; an eg of how this makes a difference in practice is a popup containing a list, which has a low word count but is very tall for its word count, so it needs a bottom button
var pupx2 = pupx.cloneNode(true); // clone the 1st close button
pupx2.className += ' end'; // append "end" classname
pupx2.onclick = function(){toglDisp(this.parentElement);} // the onclick cannot logically be cloned (apparently) so it has to be done specifically for this button
popup.appendChild(pupx2); // inject the new close button
}
if (huntingBugs) console.log("done making popup #: " + pupnum + "\n\n\n");
}
if (huntingBugs) console.log("=== end of popup loop\n\n");
console.log("POPUPS detectinated " + pupbs.length + " buttons & " + pups.length + " popups" );
}
function toglDispId(id) {
id = id.replace(/open-/, ""); // given the id of a popup button, strip off the open- prefix to match the id of the target popup
if (obj = document.getElementById(id))
obj.style.visibility = (obj.style.visibility == 'visible' ? 'hidden':'visible');
console.log('I am making things in/visible!');
}
function toglDisp(obj) {
obj.style.visibility = (obj.style.visibility == 'visible' ? 'hidden':'visible');
}
function assignSizeClasses(popup) { // counts the words in the popup and provides a size class
var popupGuts = popup.innerHTML; // get the popup content
var popupGuts = popupGuts.replace(/<\/?[^>]+(>|$)/g, ""); // strip markup from content
var popupWordCount = Math.round(popupGuts.length/7); // count the words in the content
if (huntingBugs) console.log("~" + popupWordCount + " words [" + popupGuts.substr(0,10) + "…]");
if (popup.className.search("big") == -1 && popupWordCount > 200) { // if 'big' class hasn't been set and wordcount is high
if (huntingBugs) console.log('added \'big\' class to popup');
return ' big'; // add 'big' class
}
else
if (popup.className.search("sml") == -1 && popupWordCount < 80) { // if 'sml' class hasn't been set and wordcount is low
if (huntingBugs) console.log('added \'sml\' class to popup');
return ' sml'; // add 'sml' class
}
else {
if (huntingBugs) console.log('no size class present/added to popup');
return ''; // return an empty string
}
}
function zapPopups () { // invoked by either ESC press or click outside any popup
//alert('zapping da pups');
zoomOut(document.getElementById("ZoomBox")); // get rid of zoomed images (there’s only ever one at a time)
// footnotes must be hidden by banishment from the DOM (toggleFootnote), but generic popups can be booted with just visibility:hidden; awkwardly, although they share the classname 'popup', they do not work the same way; footnotes must be handled first
var openFns = document.getElementsByClassName('popup footnote'), i;
if (openFns.length > 0) {
for (var i = openFns.length-1; i >= 0; --i) { // work backwards
var noteID = openFns[i].id;
// console.log(i + ". You pressed ESC, you scurvy dog! And we found note \"" + noteID + "\" open!");
var noteID = noteID.match(/\d+/g); // extract the note number from the id of form "noteX-clone"
toggleFootnote(noteID); // use the toggleFootnote function (from footnoter-js) to make the note go buh-bye
return false; // only close one footnote or generic popup at a time
}
}
var popups = document.getElementsByClassName('popup'), n;
for (var n = popups.length-1; n >= 0; n--) { // work backwards
// console.log(popups.length + " popups on the page, and popup " + n + " is " + popups[n].style.visibility);
if (popups[n].style.visibility == 'visible') {
popups[n].style.visibility = 'hidden';
break;
}
}
}