Improved Image Gallery Block in Item Details Page

I’m not satisfied with image gallery (and generally image blocks) in item details page. The sliders used in image galleries are not so user friendly when browsing from desktop (too little, not so common, and sometimes appears hidden into the picture itself) and the resize/alignment options miss some basic features. Also the behavior of blocks changes when set into “open in new tab” (or same tab) vs modal.
I’d like something like a main picture with left-right arrows to navigate without clinking into the picture and a small gallery below to help the viewer see all the gallery’s pictures.
See example below:


Thanks

3 Likes

Hi @Ricky thank you for the suggestion. I will add that as a feature request

@Ricky et al,

I’d welcome a native robust image gallery as well! In the meantime, I’ve cobbled together a masonry gallery with lightbox to suit a current project of mine.

I have an Airtable attachment field containing my desired photos, and on my details page, I pull this field in via a normal image gallery field, using a javascript to hide it and its parent.

Note that it’s the second n gallery, as my details page has a primary photo gallery as its header.

Then, I have a script that rips all the network resources from the hidden, full gallery of photos, and loads them into a lightbox/masonry gallery as such via a custom code block that contains the div below. Note that the script ignores the first resource (to avoid a redundant image load that’s already in the header).

<div class="gallery">
    <div class="gallery-sizer"></div>
</div>


<script>
  window.addEventListener('load', () => {
    let attempt = 0;
    const maxAttempts = 10;
    const intervalTime = 300;

    // Function to hide the second occurrence of .gallery-container and its parent container
    const hideSecondGalleryContainer = () => {
      const galleryContainers = document.querySelectorAll('.gallery-container');
      if (galleryContainers.length > 1) {
        const secondGalleryContainer = galleryContainers[1];
        const parentContainer = secondGalleryContainer.parentElement;
        parentContainer.style.display = 'none';
        console.log('Second .gallery-container and its parent hidden.');
      } else {
        console.log(`Attempt ${attempt + 1}: .gallery-container not found.`);
        attempt++;
        if (attempt < maxAttempts) {
          setTimeout(hideSecondGalleryContainer, intervalTime);
        } else {
          console.log('Max attempts reached. .gallery-container not found.');
        }
      }
    };

    // Initial call to the function
    hideSecondGalleryContainer();
  });

</script>

<!-- Lightbox2 CSS -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/css/lightbox.min.css" rel="stylesheet" />

<!-- Lightbox2 JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/js/lightbox.min.js"></script>

<!-- Masonry JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/masonry/4.2.2/masonry.pkgd.min.js"></script>

<script>
    window.addEventListener('load', () => {
        let imageUrls = [];

        // Function to extract images from the document
        const extractImages = () => {
            const initialImgs = document.querySelectorAll('img');
            initialImgs.forEach((img) => {
                if (img.src.startsWith('https://v5.airtableusercontent.com')) {
                    imageUrls.push(img.src); // Add image URL to the array
                }
            });
        };

        // Create a MutationObserver to watch for changes in the network requests
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.addedNodes.length) {
                    mutation.addedNodes.forEach((node) => {
                        if (node.nodeType === 1) { // Check if the node is an element
                            // Look for img elements
                            const imgElements = node.querySelectorAll('img');
                            imgElements.forEach((img) => {
                                if (img.src.startsWith('https://v5.airtableusercontent.com')) {
                                    imageUrls.push(img.src); // Add image URL to the array
                                }
                            });
                        }
                    });
                }
            });
        });

        // Observe changes in the body of the document
        observer.observe(document.body, {
            childList: true,
            subtree: true,
        });

        // Initial extraction in case images are already present
        extractImages();

        // Wait for a short duration to ensure all images are captured
        setTimeout(() => {
            imageUrls.shift(); // Remove the first image URL
            const galleryContainer = document.querySelector('.gallery');

            imageUrls.forEach((url, index) => {
                const galleryItem = document.createElement('div');
                galleryItem.classList.add('gallery-item');
                const link = document.createElement('a');
                link.href = url;
                link.setAttribute('data-lightbox', 'gallery'); // Lightbox attribute
                link.setAttribute('data-title', `Image ${index + 1}`); // Optional: Add title
                const img = document.createElement('img');
                img.src = url;
                link.appendChild(img);
                galleryItem.appendChild(link);
                galleryContainer.appendChild(galleryItem);
            });

            // Initialize Masonry after images are added
            new Masonry(galleryContainer, {
                itemSelector: '.gallery-item',
                columnWidth: '.gallery-sizer', // Ensure the column width is set properly
                percentPosition: true,
                gutter: 20 // Add vertical gaps
            });
        }, 2000); // Adjust the delay as needed
    });
</script>

 <style>
    .gallery {
        width: 100%;
        margin: 0 auto;
    }
    .gallery-sizer,
    .gallery-item {
        width: calc(25% - 20px); /* Adjusted for the gap */
        box-sizing: border-box;
    }
    .gallery-item {
        margin-bottom: 20px; /* Vertical gap */
    }
    .gallery-item img {
        width: 100%;
        display: block;
    }
    @media (max-width: 993px) {
        .gallery-sizer,
        .gallery-item {
            width: calc(50% - 20px);
        }
    }
    @media (max-width: 600px) {
        .gallery-sizer,
        .gallery-item {
            width: calc(100% - 20px);
        }
    }
 
    .lb-caption {
        display: none !important;
    }
    </style>

While it’s not the typical slider gallery you are requesting, it hopefully helps demonstrate the power of custom blocks and custom headers/footers.

Cheers!