Note: I’ve moved away from the solution outlined in this post. Mainly because I did not want to write an entire gallery from scratch. I’ve since opted to use lightgallery.js which is a pure JS image gallery solution with some nice goodies.
The premise is simple. A post may contain images. These images are restricted in rendered size to keep the flow of the page in tact. Clicking an image allows you to zoom in. Here’s an example:
Go ahead, click that bunny!
The CSS#
Let’s get the CSS out of the way first. The selector used is article img
, which means
any image in the post. By default I limit it to a maximum width of its parent container.
Also, I change the cursor to a pointer, to indicate you can click on the image, like a link.
article img {
max-width: 100%;
cursor: pointer;
}
Then there are images with the zoomed
class. This is still the same image element,
but with an additional class:
article img.zoomed {
position: fixed;
top: 5vh;
bottom: 5vh;
left: 5vw;
right: 5vw;
max-width: 90vw;
max-height: 90vh;
margin: auto;
border: 4px solid #000
}
Okay, that’s a bit more CSS, but this basically overlays the image on to the page and adds some whitespace around it.
The trick to zooming is adding the zoomed
class to the img
element. Zooming out means
removing that zoomed
class again.
Now, on to the JavaScript…
The jQuery solution#
For years now jQuery has been my go-to tool for anything JavaScript, mainly because it comes bundled with Rails. (Yes, I used prototype as well in the old days.)
The jQuery solution is as you rather straight forward. Wait for the DOM to be loaded,
and handle click
events on all article img
elements. When clicked, toggle the zoomed
class.
$(function() {
$(document).on('click', 'article img', function() {
$(this).toggleClass('zoomed');
});
}
Because I’m a keyboard user (Vim, not Emacs, thank you), I prefer to map ESC to also close any zoomed imaged.
$(function() {
$(document).keyup(function(e) {
if (e.keyCode == 27) {
$('img.zoomed').each(function(idx) {
$(this).toggleClass('zoomed');
});
}
}
}
Again, hook into the keyup
event, check if ESC was pressed and toggle
the zoomed
class for all zoomed in images.
But, using jQuery means adding an extra dependency: 1 extra HTTP request and 85kB download for you. Also, the few friends I have who practice JavaScript tell me that pure JavaScript is the way to go these days. So, let’s try!
The JavaScript solution#
With some help from the You Might Not Need jQuery website, I managed to drop the 85kB big jQuery dependency and rewrite the above functionality in plain old JavaScript.
First, let’s write a function that waits for the DOM to load.
function ready(fn) {
if (document.readyState != 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
This was taken straight from You Might Not Need jQuery to wrap any functions you want to run when the document has loaded fully.
Next I wrote a function to handle toggling the zoomed
CSS class on the images:
function imageClick(e) {
e.preventDefault();
this.classList.toggle('zoomed');
}
It turns out that JavaScript is more than capable of toggling classes on its own.
While we’re at it, let’s also write the function that handles the ESC presses.
function handleEsc(e) {
if (e.keyCode == 27) {
var zoomedImages = document.querySelectorAll('img.zoomed');
Array.prototype.forEach.call(zoomedImages, function(el, i) {
el.classList.toggle('zoomed');
});
}
}
This is a bit more involed. I still check for the proper keyCode
, and then proceed to
find all zoomed images using document.querySelectorAll
. It’s really that easy.
Next I use the Array
prototype to map a function to each zoomed image. That function simply
toggles the zoomed
class, just like imageClick
does.
What remains is nothing more than some glue to put the above fuctions together.
ready(function() {
var images = document.querySelectorAll('article img');
Array.prototype.forEach.call(images, function(el, i) {
el.addEventListener('click', imageClick);
});
document.addEventListener('keyup', handleEsc);
});
Here I use the ready
function I wrote. Just like handleEsc
, I find all
article img
elements and add the event listener for clicks. Then I also
add an event listener for the ESC key.
Conclusion#
Rewriting a trivial piece of jQuery code to plain JavaScript appears to be more than worth the while. Besides the warm fuzzy feeling of dumping jQuery, it saves quite a few kilobytes from each page on devroom.io. Especially for mobile users this matters.
git
says 11 deletions (bye, jQuery) and 27 additions (hello, JavaScript). This does
not tell the full story, as one of these deleted lines is this one:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
I can highly recommend you take a closer look at what your jQuery code is actually doing and consider moving away from unnecessary dependencies. Yay for lean and mean web pages!