The Royal Association – Gridzy.js & PhotoSwipe

Because Gridzy.js is very cautious and flexible, it can be combined with many many lightboxes. PhotoSwipe is one of the most responsive and performant lightbox scripts. Let’s combine them! 😉


Start with Gridzy.js

1. Upload the Gridzy files to your server and add them to your website (usually in the head section):

<link rel="stylesheet" href="gridzy/gridzy-2.0.min.css" />
<script src="gridzy/gridzy-2.0.min.js"></script>

2. Add the HTML code (anywhere in the body section):

<div class="gridzy">
	<a href="path-to-image-1-big.jpg" data-size="1920x1080">
		<img class="gridzyImage" src="path-to-image-1-small.jpg" alt="a random image" />
	</a>
	<a href="path-to-image-2-big.jpg" data-size="1920x1080">
		<img class="gridzyImage" src="path-to-image-2-small.jpg" alt="another random image" />
	</a>
	<a href="path-to-image-3-big.jpg" data-size="1920x1080">
		<img class="gridzyImage" src="path-to-image-3-small.jpg" alt="yet another random image" />
	</a>
	<a href="path-to-image-4-big.jpg" data-size="1920x1080">
		<img class="gridzyImage" src="path-to-image-4-small.jpg" alt="a random image again" />
	</a>
	<a href="path-to-image-5-big.jpg" data-size="1920x1080">
		<img class="gridzyImage" src="path-to-image-5-small.jpg" alt="one last random image" />
	</a>
</div>

As you can see, the images are encapsulated in a-tags. Additionally you need to consider these things:

  • The href attribute should contain the URL to the large image.
  • The data-size attribute should contain the original size of the large image.
  • The CSS class gridzyImage is needed for all img tags.
  • The src attribute should contain the URL to the small image.

Then add PhotoSwipe

First you need to download PhotoSwipe from GitHub. Just unpack the ZIP archive and copy the “dist” folder into your project. You can rename the folder from “dist” to “photoswipe” for example, so you can’t get confused about this folder.

Step 1: include JS and CSS files

Add the following to your website (usually in the head section):

<link rel="stylesheet" href="photoswipe/photoswipe.css"> 
<link rel="stylesheet" href="photoswipe/default-skin/default-skin.css"> 
<script src="photoswipe/photoswipe.min.js"></script> 
<script src="photoswipe/photoswipe-ui-default.min.js"></script> 

Step 2: add PhotoSwipe HTML

Add the following directly before the closing </body>:

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                      <div class="pswp__preloader__cut">
                        <div class="pswp__preloader__donut"></div>
                      </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div> 
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

Step 3: initialize

Add the following also directly before the closing </body> (so, between the HTML, you just added, and the closing </body>):

<script>
document.addEventListener('DOMContentLoaded', function() {
	// Polyfills start
	if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
	if (!Element.prototype.closest) { Element.prototype.closest = function(s) { var el = this;
	do { if (el.matches(s)) return el; el = el.parentElement || el.parentNode; } while (el !== null && el.nodeType === 1); return null; }; }
	// Polyfills end

	// get PhotoSwipe element
	var pswpElement = document.querySelectorAll('.pswp')[0];
	
	// add click event listener
	document.body.addEventListener('click', function(event) {
		var alias = event.target ? event.target.closest('.gridzy a') : null;
		if (alias) {
			event.preventDefault();
			openPhotoSwipe(alias.closest('.gridzy'), alias);
		}
	});
	
	// open picture from URL if directly requested
	setTimeout(function() {
		var hash = location.hash.substring(1).split('&').map(function(val) { return val.split('='); });
		var pos = hash.length, param = {}, gridzyElement, itemElement;
		while (pos--) {
			param[hash[pos][0]] = hash[pos][1];
		}
		if ('gid' in param && 'pid' in param) {
			gridzyElement = document.querySelector((param.gid.match(/^[0-9]+$/) ? '' : '#' + param.gid + '.gridzy, ') + '.gridzy[data-gridzyid="' + param.gid + '"]');
			itemElement = gridzyElement.querySelector((param.pid.match(/^[0-9]+$/) ? '' : '#' + param.pid + '.gridzyItem, ') + '.gridzyItem[data-gridzyitemid="' + param.pid + '"]');
			openPhotoSwipe(gridzyElement, itemElement);
		}
	}, 0);
	
	// initialize and open PhotoSwipe
	function openPhotoSwipe(gridzyElement, itemElement) {
		var itemElements = gridzyElement.children;
		var pos = itemElements.length;
		var items = [], img, size, title, startImg, thumbnails = [], options, gallery;
		// prepare items
		while (pos--) {
			if (itemElements[pos].classList.contains('gridzyItemHidden')) {
				continue;
			}
			img = itemElements[pos].querySelector('img.gridzyImage');
			if (size = itemElements[pos].getAttribute('data-size')) {
				size = itemElements[pos].getAttribute('data-size').split('x');
				title = itemElements[pos].getAttribute('data-title');
				title = title ? title : ((title = itemElements[pos].querySelector('.gridzyCaption')) ? title.innerText : '');
				items.unshift({
					src: itemElements[pos].href,
					w: parseInt(size[0], 10),
					h: parseInt(size[1], 10),
					msrc: img.src,
					title: title,
					pid: itemElements[pos].hasAttribute('id') ? itemElements[pos].id : itemElements[pos].getAttribute('data-gridzyitemid')
				});
				thumbnails.unshift(img);
				if (itemElement === itemElements[pos]) {
					startImg = img;
				}
			}
		}
		// prepare options
		options = {
			index: thumbnails.indexOf(startImg) || 0,
			galleryUID: (gridzyElement.hasAttribute('id') ? gridzyElement.id : gridzyElement.gridzy.id),
			getThumbBoundsFn: function(index) {
				var pageYScroll = window.pageYOffset || document.documentElement.scrollTop;
				var rect = thumbnails[index].getBoundingClientRect();
				return {x:rect.left, y:rect.top + pageYScroll, w:rect.width};
			}
		};
		// open PhotoSwipe
		gallery = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options);
		gallery.init();
	}
});
</script>

That’s it!

The positions where to add the code snippets are only proposals. So, feel free to change the order and positions. You can also put the last code snippet into a JS file for example. The order of the snippets shouldn’t be relevant.

Have fun! ✌😊

Updates:

March 15, 2019
Changed the initialization code to only show images in the lightbox, that are not hidden by the Gridzy.js filter feature.

March 16, 2019
Fixed an issue in the initialization code. The start image (shown when opening the lightbox) wasn’t determined correctly anymore after yesterdays changes.

Leave a reply …

or

You can post as a guest if you like. But please consider, that I always review such comments before I switch them public, to prevent spam. If you want to see your comment public immediately, just sign up and in. It's just a click away and it's really simple. You don't even need a password. 😉

3 thoughts on “The Royal Association – Gridzy.js & PhotoSwipe

  1. Hi Helmut,

    Nice code, works well. Am trying to set the layout to ‘waterfall’, have added layout:’waterfall’ to options = {} but this doesn’t help. Is there a way to do this?

    Thanks

    Paul

    1. Hi Paul,

      thank you for your question! The options variable in this example are the options for PhotoSwipe. So for Gridzy you need to add it elsewhere.

      Just add the attribute data-gridzy-layout=”waterfall” to the container div element. Like this: <div class="gridzy" data-gridzy-layout="waterfall">

      All the best,
      Helmut

  2. Hello Helmut,

    This is an amazing combination and works like a charm. I love the action. Just wondering whether, there is a way to make the above work with srcset so we can serve bigger thumbnails to say 4k displays and smaller to mobiles.

made by @eHtmlu