Handling images and galleries in Fancybox 3

A few years back I wrote about how to get Fancybox 1 working on WP galleries – specifically how to add a rel tag to an image anchor tag so that images would appear grouped in their correct galleries.

That all became obsolete with the introduction of the Gutenberg block editor, as images and galleries are now inserted into posts and pages in completely different ways.

Before, there were plenty of filters available to change image and wrapping anchor tags relatively easily, but my attempts to do something server-side and php-based with the block editor met with total failure. Instead I ended up upgrading to Fancybox 3 and using jQuery to sort pretty much everything out.

Here’s what I wanted:

  1. On click of any image in a gallery (whether created by the new block editor or the classic editor), that image should open up to full size and into the Fancybox gallery mode. This should just happen automatically without adding extra classes (or anything else) when creating the gallery.
  2. When inserting an image in a post, be able to choose easily whether or not that image should be openable into Fancybox viewing mode.
  3. The featured image of a post should always open in Fancybox when clicked.
  4. For all images / galleries, be able to choose whether or not a title is displayed in the Fancybox viewing mode – and to be able to choose differently for different locations. In other words I wanted to be able to have the same image inserted in two different locations, but still be able to turn on or off the title. The default option should be for title to be displayed.

I didn’t succeed in absolutely everything, but I’m not too far off. Taking each one in turn…

Opening gallery images in Fancybox

Opening a gallery image with Fancybox is easy enough with jQuery. That’s just about finding the right selectors for the new and old style galleries:

// jQuery code to be included in my initialise-fancybox.js file that is
// enqueued in my child theme functions.php file
var fbOpts = {
	// Fancybox options in here
}
$('.wp-block-gallery .blocks-gallery-item a, .gallery-icon a').fancybox( fbOpts );

Then to make images open in a gallery (and only with other images in the same gallery), we need to give each image anchor tag a data-fancybox attribute with a value corresponding to that gallery. Again, easy enough:

// Give all gallery image links a data-fancybox attribute with gallery id, both 
// block style gallery and classic editor style.
var galleryId = 1;
$('.wp-block-gallery').each( function() {
	$(this).find('.blocks-gallery-item a').each( function() {
		$(this).attr( 'data-fancybox', 'st-gallery-' + galleryId );
	} );
	galleryId++;
} );
$('div.gallery').each( function() {
	$(this).find('div.gallery-icon a').each( function() {
		$(this).attr( 'data-fancybox', 'st-gallery-' + galleryId );
	} );
	galleryId++;
} );

Opening (or not) single images with Fancybox

Both the block and classic editors give us the choice of wrapping an image in an anchor tag (directed at the full version of the image) or not, so that’s half the battle. The other half is to add in selectors to the Fancybox initialization to pick up the links on the single images added by the new and old editors. No problem for the block editor – we can do that with .wp-block-image a – but there isn’t a useful container we can zoom in on for an image added with the classic editor. However we do get the option to add CSS classes to an image’s link when adding an image to a post / page – and that’s specific to that occurrence in that particular insertion, which is ideal.

My solution is that when using the classic editor and inserting an image that I want to open in Fancybox, I must remember to add the st-fancybox-link class to the link. And the initialise-fancybox.js file now needs to include the two selectors for single images inserted with both kinds of editors:

var fbOpts = {
	// Fancybox options in here
}
$('.wp-block-image a, .st-fancybox-link, .wp-block-gallery .blocks-gallery-item a, .gallery-icon a').fancybox( fbOpts );

Featured image should open in Fancybox

This will probably be theme-specific, and it certainly is in my case. I’m using the Hoot Ubix theme, and it inserts a featured image at the top of the post without giving the option to give it a link. So a bit of php work needed. The theme code uses the_post_thumbnail( $size, $attr ) function to display the featured image, and digging into the WP code a bit reveals that the html can be amended using the post_thumbnail_html filter.

This is the code that ended up in my child theme functions.php file:

/**
 * Add a link to post thumbnail so that featured image has a
 * wrapping anchor tag with appropriate class ready for Fancybox.
 * @hooked post_thumbnail_html
 */
function st_add_link_to_post_thumbnail( $html, $post_id, $post_thumbnail_id, $size, $attr ) {
	// If this is the featured image for a post, add the link.
	if ( isset( $attr['class'] ) ) {
		if ( strpos( $attr['class'], 'entry-content-featured-img' ) !== false && strpos( $attr['class'], 'entry-grid-featured-img' ) === false ) {
			$title = get_the_title( $post_thumbnail_id );
			$img = wp_get_attachment_image_src( $post_thumbnail_id, 'full' );
			$html = '<a href="' . esc_url( $img[0] ) . '" class="st-featured-img-link">' . $html . '</a>';
		}
	}
	return $html;
}
add_filter( 'post_thumbnail_html', 'st_add_link_to_post_thumbnail', 10, 5 );

There was one little wrinkle to overcome, handled by checking that the class already added to the img tag is entry-content-featured-img and not entry-grid-featured-img. This is in effect making sure that the user isn’t on the Archive page displaying a list of posts, and is on the post page itself. On the Archive page, my Hoot Ubix theme displays each post’s featured image alongside each post title, and each of those featured images has a link to it’s corresponding post. I didn’t want to change that: I’m only interested in fiddling with the featured image html when the image is appearing on the post itself.

So with the changes to functions.php I just need to add in the new Fancybox trigger class to my js:

var fbOpts = {
	// Fancybox options in here
}
$('.st-featured-img-link, .wp-block-image a, .st-fancybox-link, .wp-block-gallery .blocks-gallery-item a, .gallery-icon a').fancybox( fbOpts );

Title display (or not)

The trick to getting Fancybox to display a title is to set a data-caption attribute in the link to the image file. Since, as I’ve already mentioned, I struggled to find a (simple) php way to amend the image html, again jQuery was my answer. Each type of image had to be handled differently though.

The single image was easy enough – just copy the value of the image title attribute to a data-caption attribute in the image link:

// Give wrapping anchor tag image title from title attribute for single images.
$('.wp-block-image img, .st-fancybox-img img').each( function() {
	var title = $(this).attr('title');
	if ( typeof( title ) != 'undefined' ) {
		if ( title.length ) {
			var parentAnchor = $(this).parent('a');
			if ( typeof( parentAnchor ) != 'undefined' ) {
				parentAnchor.attr( 'data-caption', title );
			}
		}
	}
} );

The only thing to watch out for is that the title attribute does need to be added each time you insert an image, whether using classic or block editor. This is a good thing though – it means you can use different titles for the same image when you display it in different places. Or not display it at all.

Now for the featured image. Hmm, well I could also add in the data-caption attribute when I add the anchor tag with my php post_thumbnail_html filter (see above). Trouble is, I then can’t easily turn it off on a case-by-case basis. I would have been delighted if I could add a class to the featured image when I set it in the editor, because then I could add a class such as st-no-fancybox-title and use jQuery to pick that up and remove the data-caption attribute. But I don’t have that option. I did think of increasingly complex solutions – to the point of adding a metabox to posts giving the option to show / hide featured image title. That could have added a class to the post content container itself, say, which could then be picked up by jQuery to add or exclude the data-caption attribute on the link as appropriate. But all too complicated, and not worth the effort as I decided it was unlikely I’d ever be desperate for the featured image title to show. So conclusion is – featured images don’t display titles.

Lastly, titles in galleries.

Neither the new block editor nor the old classic editor give the option to set title attributes for gallery images, but they do allow you to use (or not) a caption on each thumbnail, so the obvious approach was to copy that caption into the data-caption attribute of the link.

There is a slight complication with the classic editor. The caption is stored in the image’s postmeta info in a one-to-one relationship with the image. That means if you use the image in several galleries, the caption will always be the same and is either on or off in all cases. Bit annoying, but it’s not a big deal as we’re only dealing with the legacy editor – any galleries where this is a problem you can always convert the post to the block editor…

Anyway, back to copying the caption into the data-caption attribute of the link. For the block editor, the caption appears in a figcaption element immediately following the link, and for the classic editor the figcaption element follows a div that wraps the link and image, so the jQuery code to sort that went like this:

// Set data-caption attribute for image links in galleries by copying caption.
$('.blocks-gallery-item a').each( function() {
	$(this).attr( 'data-caption', $(this).next('figcaption').text() );
} );
$('.gallery-icon').each( function() {
	$(this).children('a').first().attr( 'data-caption', $(this).next('figcaption').text() );
} );

(Incidentally the use of text() instead of html() is so that only the title text is used and no html formatting from the gallery caption is pulled across into the Fancybox title formatting.)

And that, as they say, is that. All images opening all luvverly in the shiny new Fancybox! A single image and then a gallery below as an example, followed by the complete file of initialise-fancybox.js…

Equinox
/*Initialize Fancybox */
jQuery( function( $ ) {
	
	// Give wrapping anchor tag image title from title attribute for single images.
	$('.wp-block-image img, .st-fancybox-link img').each( function() {
		var title = $(this).attr('title');
		if ( typeof( title ) != 'undefined' ) {
			if ( title.length ) {
				var parentAnchor = $(this).parent('a');
				if ( typeof( parentAnchor ) != 'undefined' ) {
					parentAnchor.attr( 'data-caption', title );
				}
			}
		}
	} );

	// Give all gallery image links a data-fancybox attribute with gallery id, both 
	// block style gallery and classic editor style.
	var galleryId = 1;
	$('.wp-block-gallery').each( function() {
		$(this).find('.blocks-gallery-item a').each( function() {
			$(this).attr( 'data-fancybox', 'st-gallery-' + galleryId );
		} );
		galleryId++;
	} );
	$('div.gallery').each( function() {
		$(this).find('div.gallery-icon a').each( function() {
			$(this).attr( 'data-fancybox', 'st-gallery-' + galleryId );
		} );
		galleryId++;
	} );
	
	// Set data-caption attribute for image links in galleries by copying caption.
	$('.blocks-gallery-item a').each( function() {
		$(this).attr( 'data-caption', $(this).next('figcaption').text() );
	} );
	$('.gallery-icon').each( function() {
		$(this).children('a').first().attr( 'data-caption', $(this).next('figcaption').text() );
	} );
	
	// Initialise all images for fancybox.
	var fbOpts = {
		// Fancybox options in here
	};
	$('.st-featured-img-link, .wp-block-image a, .st-fancybox-link, .wp-block-gallery .blocks-gallery-item a, .gallery-icon a').fancybox( fbOpts );

} );

Leave a Reply

Your email address will not be published. Required fields are marked *