Pure Javascript Lightbox / Image Popup Modal
I wish to share a Pure Javascript Lightbox or Image Popup Modal which is completely compliant with the Navigation guidelines of ARIA and is lightweight and easy to implement because :
- It does not require any JQUERY hence an additional import request to CDN and the buffering of entire JQUERY library can be prevented.
- This is a deploy and forget kind of design where we only need to add the LIGHTBOX related HTML, CSS and JAVASCRIPT without any need to add unique IDs or CLASSes to the HTML tags of images or its parent A tag. That is why, later addition or removal of images from your exisitng webpages will not require any change in the Javascript or the ID/CLASS attributes of IMG/A tags.
To implement this, a precursor is that all the images in IMG tag shall be the only Child node of a parent A tag like <a title="" href=""><img alt="" src=""/></a>
.
Also, One thing to keep in mind is that there should be no White spaces in between opening and closing of A tag and IMG tag and it should appear as <a><img/></a>
.
Since only IMG tag images are loaded when page is downloaded while A tags are not that is why creating a parent A tag for all the IMG tag allows to store smaller size of the images in IMG tag and larger size of the same images in the parent A tag.
Combining IMG tags with Loading="lazy"
will help further speed up the page loading.
This Lightbox is simple in working when implemented on a web page i.e. you Click, Touch or press Enter key on any image of IMG tag on page and a Modal or Lightbox will Popup to display the same URL image or a different one stored in the parent A tag of the IMG tag.
- Same or Different URL for storing different quality images depend upon your choice. Former is easier to implement while later has a better performance.
Now let us see some code :
This a Sample HTML of A tags containing Images and URL to other pages.
<a href="https://1.bp.blogspot.com/-jNZtfo_qgNM/YHH_XF6FooI/AAAAAAAAC5E/y_tNUslqFPITSVncCkF3zyEC-RROSvETgCLcBGAsYHQ/s328/1.jpg" title="first image"><img alt="first image" src="https://1.bp.blogspot.com/-jNZtfo_qgNM/YHH_XF6FooI/AAAAAAAAC5E/y_tNUslqFPITSVncCkF3zyEC-RROSvETgCLcBGAsYHQ/s320/1.jpg"/></a>
<a href="https://1.bp.blogspot.com/-F0sshQGKu8Y/YHH_XL41aDI/AAAAAAAAC5M/fyAeo4X2tFw-RN-g8YFxNcel0WivQjj5gCLcBGAsYHQ/s400/2.jpg" title="second image"><img alt="second image" src="https://1.bp.blogspot.com/-F0sshQGKu8Y/YHH_XL41aDI/AAAAAAAAC5M/fyAeo4X2tFw-RN-g8YFxNcel0WivQjj5gCLcBGAsYHQ/s320/2.jpg"/></a>
<a href="https://1.bp.blogspot.com/-xk_pNZ1fh7o/YHH_XEROwmI/AAAAAAAAC5I/-WOsyfKgtSMRzXQBeEX-yjRX8TBJuEkFwCLcBGAsYHQ/s400/3.jpg" title="third image"><img alt="third image" src="https://1.bp.blogspot.com/-xk_pNZ1fh7o/YHH_XEROwmI/AAAAAAAAC5I/-WOsyfKgtSMRzXQBeEX-yjRX8TBJuEkFwCLcBGAsYHQ/s320/3.jpg"/></a>
<a href="https://1.bp.blogspot.com/-e3gjnYR7exE/YHH_YHRQgLI/AAAAAAAAC5Q/kgQYFsvBjuYPAXTjzMFkzvsRT6JgQlkywCLcBGAsYHQ/s720/4.jpg" title="fourth image"><img alt="fourth image" src="https://1.bp.blogspot.com/-e3gjnYR7exE/YHH_YHRQgLI/AAAAAAAAC5Q/kgQYFsvBjuYPAXTjzMFkzvsRT6JgQlkywCLcBGAsYHQ/s320/4.jpg"/></a>
<a href="https://1.bp.blogspot.com/-zGPorKCAHqw/YHH_YHcIpSI/AAAAAAAAC5U/Jx2serYqk58fa_HSf1GPwDZu2nT1c8kJgCLcBGAsYHQ/s1280/5.jpg" title="fifth image"><img alt="fifth image" src="https://1.bp.blogspot.com/-zGPorKCAHqw/YHH_YHcIpSI/AAAAAAAAC5U/Jx2serYqk58fa_HSf1GPwDZu2nT1c8kJgCLcBGAsYHQ/s320/5.jpg"/></a>
<a href="https://anubhavyadavjovian.blogspot.com/">Anubhav yadav</a>
Let say we have five images on our webpage and all are in the format <a title="" href=""><img alt="" src=""/></a>
as defined above.
To show that this Lightbox is dynamic, we have included an additional <a href="https://anubhavyadavjovian.blogspot.com/">Anubhav yadav</a>
, which will behave like a normal A Href Tag when Clicked, Touched or Enter key is pressed on it, while the A tags with only IMG tag as a Child will popup the Lightbox.
<div id='lightbox-home'>
<div id='lightbox-container' onclick='hideoverlay()'>
<img alt='' id='lightbox-cont-img' onclick='hideoverlay()' src=''/>
</div>
</div>
The Real HTML code above is for our Lightbox with a "lightbox-container" ID appearing as a translucent black background with an image displaying tag with "lightbox-cont-img" ID.
#lightbox-container {
z-index:2000;
position:fixed;
bottom:-5000px;
left:0px;
width:100%;
height:100%;
margin:0px;
background-color: rgba(38, 38, 38, 0.85);
transition: all 0.4s ease-out;
display:flex;
flex-direction:column;
justify-content:center;
align-items:center;
}
#lightbox-container.showcontainer {
bottom:0px;
}
#lightbox-container img {
max-width:95%;
max-height:95%;
object-fit:contain;
}
:focus {
border: 2px solid gold;
}
Above is the Real CSS for the decoration of Lightbox as well as creating transitions while appearing and disappearing.
// Select all A tags with IMG child nodes
var atagswithimgtag = document.querySelectorAll("a[href]");
// then prevent the default behaviour of A tags by preventing of opening new page by HREF
// as well as collect all the HREF of A tags with images to enable RIGHT and LEFT arrow key
var allimgurlarray = [];
for(i=0;i<atagswithimgtag.length;i++){
var childAIMGtag = atagswithimgtag[i].childNodes;
if (childAIMGtag[0].nodeType != Node.TEXT_NODE) // or if (el[i].nodeType != 3)
{
// this seems too be a A tag with IMG tag as Childnode
// first we need to prevent the default behaviour of opening the IMG in New Tab
atagswithimgtag[i].addEventListener("click", function(event){
event.preventDefault();
});
// second is when we need to fill image URL aray with A HREF
var listofnodes = atagswithimgtag[i];
allimgurlarray[i] = [];
allimgurlarray[i][0] = i;
allimgurlarray[i][1] = " Image URL is ";//listofnodes.getAttributeNode("title").value;
allimgurlarray[i][2] = listofnodes.getAttributeNode("href").value;
}
console.log(childAIMGtag[0].innerHTML);
}
// now we have to deal with Keyboard events
document.onkeydown = function(event){
if(event.keyCode==27){ // If ESC key is pressed
if(document.getElementById("lightbox-container").classList.contains("showcontainer")){ // LIGHTBOX ON
document.getElementById("lightbox-container").classList.remove("showcontainer");
}
}
else if(event.keyCode==13) { // ENTER key pressed
if(document.getElementById("lightbox-container").classList.contains("showcontainer")){ // LIGHTBOX ON
document.getElementById("lightbox-container").classList.remove("showcontainer");
}
else { // LIGHTBOX OFF
var currentEventTarget = document.activeElement;
if(currentEventTarget.tagName=='A'){
var entertargetchild = currentEventTarget.childNodes;
if(entertargetchild[0].tagName=='IMG'){
var hrefofparent = currentEventTarget.getAttribute("href");
document.getElementById("lightbox-cont-img").setAttribute("src", hrefofparent);
document.getElementById("lightbox-container").classList.add("showcontainer");
document.getElementById("lightbox-cont-img").focus();
}
}
}
}
else if(event.keyCode==9) { // TAB key pressed
if(document.getElementById("lightbox-container").classList.contains("showcontainer")){ // LIGHTBOX ON
document.getElementById("lightbox-container").classList.remove("showcontainer");
}
}
else if(event.keyCode==37) { // Left arrow key
if(document.getElementById("lightbox-container").classList.contains("showcontainer")){ // LIGHTBOX ON
// first get the URL of image displayed in the LIGHT BOX
var currimgsrc = document.getElementById("lightbox-cont-img").getAttribute("src");
// now match the sequence number in the array
var serialofarray = 0;
for(k=0;k<allimgurlarray.length;k++){
if(currimgsrc == allimgurlarray[k][2]){
serialofarray = allimgurlarray[k][0];
}
}
// with LEFT arrow, we are supposed to reduce the sequence and then use its ATTR SRC to LIGHT BOX
if(serialofarray<=0){
serialofarray = allimgurlarray.length - 1;
}
else {
serialofarray = serialofarray - 1;
}
console.log("Left Arrow : "+serialofarray);
document.getElementById("lightbox-cont-img").setAttribute("src", allimgurlarray[serialofarray][2]);
}
}
else if(event.keyCode==39) { // RIGHT Arrow
if(document.getElementById("lightbox-container").classList.contains("showcontainer")){
// first get the URL of image displayed in the LIGHT BOX
var currimgsrc = document.getElementById("lightbox-cont-img").getAttribute("src");
// now match the sequence number in the array
var serialofarray = 0;
for(l=0;l<allimgurlarray.length;l++){
if(currimgsrc == allimgurlarray[l][2]){
serialofarray = allimgurlarray[l][0];
}
}
// with RIGHT arrow, we are supposed to increase the sequence and then use its ATTR SRC to LIGHT BOX
if(serialofarray>=allimgurlarray.length-1){
serialofarray = 0;
}
else {
serialofarray = serialofarray + 1;
}
console.log("Right Arrow : "+serialofarray);
document.getElementById("lightbox-cont-img").setAttribute("src", allimgurlarray[serialofarray][2]);
}
}
else { // If any key other than ESC is pressed
if(document.getElementById("lightbox-container").classList.contains("showcontainer")){
document.getElementById("lightbox-container").classList.remove("showcontainer");
}
}
}
// through this we are handling the CLICK ON IMAGE events
document.onclick= function(event) {
overlaypop(event);
};
function overlaypop(event) {
if (event===undefined) event= window.event;
var targettag = event.target;
var targetparent = event.target.parentNode;
if(targettag.tagName=='IMG'){
if(targetparent.nodeName=='A'){
event.preventDefault();
var hrefofparent = targetparent.getAttribute("href");
//alert('clicked on '+ targettag.tagName + ' parent name is ' + targetparent.nodeName + ' and URL is ' + hrefofparent);
document.getElementById("lightbox-cont-img").setAttribute("src", hrefofparent);
document.getElementById("lightbox-container").classList.add("showcontainer");
document.getElementById("lightbox-cont-img").focus();
}
}
}
function hideoverlay() {
document.getElementById('lightbox-container').classList.remove('showcontainer')
}
With the Javascript above, we wish to achieve following purposes.
- Popup image on full screen with a translucent black background when
any image is Touched, Clicked or Pressed Entered upon after
navigating to the desired image using the TAB key.
- Hide this Lightbox when Touched, Clicked anywhere on the screen or
any key on Keyboard, except Left and Right Arrow keys, is pressed.
- Navigate through all the images on a webpage available in the format
of with Left and Right
arrow key.
Briefly, let us see how this script achieves our purposes by understanding various parts of this Javascript.
With document.querySelectorAll("a[href]")
, we wish to get all the
IMG tags with Parent A tag in an array named atagswithimgtag
.
- We will use this array, first to disable the default behaviour of opening A tag in a new page by using
event.preventDefault()
.
- Then we will use this array to create a new 2D array with name
allimgurlarray
to store the A tag's HREF URLs and their Index number. This allows for a better trace when using the Left and Right keys.
After this, we have to handle two types of events i.e. Key press events and Touch/Click events.
- Key Press events are handled with
document.onkeydown
. Here we have to Handle Enter, Tab, Esc, Right and Left arrow keys with the help of If-Else-If conditions.
- Touch or Click Events are handled with
document.onclick
.
We use .classList.contains
to check if Lightbox is hidden or visible. And we use .classList.add
and .classList.remove
to show and hide the Lightbox respectively.
We use document.activeElement
, .tagName
and .childNodes
to identify the IMG tag and its parent A tag on which Enter key is pressed after using TAB key for navigation.
And We use window.event
, event.target
, event.target.parentNode
and .nodeName
to identify the IMG tag and its parent A tag when the respective image is clicked or touched.
To make the Lightbox more ARIA compatible we use .focus()
to bring focus on the image currently displayed in the Lightbox.
Clicking, touching or pressing any key will hide the Lightbox when it is visible.
Check this Answer to learn in details of how this Javascript will handle general Key Press events of keys like ESC, LEFT and RIGHT arrow.