DIY Image gallery for Shopify blog posts
Tags: shopify, javascript
Published: 2023-08-16 13:53:00

For the past few months, I've been busy with a Shopify website project. Recently, my client began focusing on content marketing, and over the last two weeks, they ran into an issue. They couldn't add image galleries to their blog posts using the content manager.

I did some research, and to my surprise, Shopify doesn't allow adding galleries to a blog post. All it offers is a rich text editor and the option to insert single images.

Some folks recommend switching to a dedicated blogging platform like WordPress. However, that approach didn't suit me since it would mean more effort. So, I came up with a clever and user-friendly workaround instead.

# Solution

The solution involves just one JavaScript and one CSS file. After it's all set up, you won't need any coding skills to use it. I believe even someone without coding experience can handle the setup.

# How to set up
1. Add this CSS to your website's assets. I simply made a separate file called "blog-carousel.css" in the assets folder. It only includes CSS styles for the gallery, with no need for third-party dependencies.

```
/* Slideshow container */
.slideshow__photo {
    max-width: 500px;
    position: relative;
    margin: auto;
}

.slideshow-container {
    margin-bottom: 32px;
}


/* Next & previous buttons */
.slideshow__prev, .slideshow__next {
    cursor: pointer !important;
    position: absolute;
    top: 50%;
    width: auto;
    margin-top: -22px;
    padding: 16px;
    color: white !important;
    font-weight: bold;
    font-size: 18px;
    transition: 0.6s ease !important;
    border-radius: 0 3px 3px 0 !important;
    user-select: none !important;
}

/* Position the "next button" to the right */
.slideshow__next {
    right: 0;
    border-radius: 3px 0 0 3px;
}

/* On hover, add a black background color with a little bit see-through */
.slideshow__prev:hover, .slideshow__next:hover {
    background-color: rgba(0,0,0,0.8);
}

.slideshow__thumbnails {
    display: flex;
    justify-content: center;
}

.slideshow__thumbnails img {
    max-width: 200px;
    max-height: 170px;
    margin-left: 3px;
    margin-right: 3px;
    cursor: pointer;
}
```

2.Add this js file that will initialize the galleries.

```
function slideShowChangeImage(e) {
    const newUrl = e.src;
    const img = e.parentNode.parentNode.getElementsByClassName('slideimg');
    img[0].src=newUrl;
}

function slideShowPrev(e) {
    changeToNextOrPrevImage(e, -1);
}

function slideShowNext(e) {
    changeToNextOrPrevImage(e, 1);
}

function getAllGalleryImages(parent) {
    const images = parent.getElementsByClassName('gallery-thumbnail');
    let allUrls = [];
    for (let i = 0; i < images.length; ++i) {
        allUrls.push(images[i].src);
    }
    return allUrls;
}

function changeToNextOrPrevImage(e, change){
    // get all available images

    // try to find the next one
    let allUrls = getAllGalleryImages(e.parentNode.parentNode);

    const img = e.parentNode.getElementsByClassName('slideimg');
    const currentUrl = img[0].src;

    const pos = allUrls.findIndex(v => currentUrl === v);
    let nextSlide = pos + change;
    if (nextSlide < 0) {
        nextSlide = allUrls.length - 1;
    } else {
        nextSlide = nextSlide % allUrls.length;
    }

    img[0].src = allUrls[nextSlide];
}

function initImageGalleries() {
    for (let i = 0; i <= 15; ++i) {
        createGallery(i.toString().padStart(2, '0'));
    }
}

function createGallery(id) {
    // get galleries grouped by number
    let allImages = document.querySelectorAll(`img[alt*="gallery ${id}"]`);

    let imgInCurrentGallery = [];
    let parent;

    let galleryStartNode = null;

    // delete the old images
    for(let i = 0; i < allImages.length; ++i) {
        const img = allImages[i];

        imgInCurrentGallery.push(allImages[i].cloneNode(true));

        parent = img.parentNode.parentNode;
        if (!galleryStartNode) {
            galleryStartNode = img.parentNode.nextSibling;
            console.log(galleryStartNode);
        }

        img.parentNode.remove();
    }

    if (allImages.length > 0) {
        buildGalleryHtml(parent, galleryStartNode, imgInCurrentGallery);
    }

    return allImages.length > 0;
}

function buildGalleryHtml(parent, insertBefore, imgs) {
    // class="slideshow-container"
    const container = document.createElement('div');
    container.className = 'slideshow-container';

    // init controls
    const controlsHtml = ` <div class="slideshow__photo">
                <div class="mySlides">
                    <img class="slideimg" src="" style="width:100%">
                </div>

                <a class="slideshow__prev" onclick="slideShowPrev(this)">&#10094;</a>
                <a class="slideshow__next" onclick="slideShowNext(this)">&#10095;</a>
                <br>
            </div>`;
    container.innerHTML = controlsHtml;

    // init thumbnails
    const thumbNailsSection = document.createElement('div');
    thumbNailsSection.className = 'slideshow__thumbnails';
    container.append(thumbNailsSection);

    for(let i = 0; i < imgs.length; ++i) {
        const img = document.createElement('img');
        img.className = 'gallery-thumbnail';
        img.src = imgs[i].src;
        img.addEventListener('click', () => slideShowChangeImage(img));
        thumbNailsSection.appendChild(img);
    }

    // set image to the first
    const img = container.getElementsByClassName('slideimg');
    img[0].src = imgs[0].src;

    parent.insertBefore(container, insertBefore);
}

document.addEventListener('DOMContentLoaded', () => initImageGalleries());
```

3.Add those two files to your website. I prefer to add it only to our blogs pages… That's what it looks for me
```
{{ 'blog-carousell.css' | asset_url | stylesheet_tag }}
<script src="{{ 'blog-carousell.js' | asset_url }}" defer="defer"></script>
```
4.If you want to put a gallery in your blog post, remember to include an "alt" attribute for your images. The "alt" attribute should end with 'gallery {id}'. The "id" can be a number between 01 and 15 (leading zeros are needed).

For example, let's say I have a few galleries within the same post. The images for the first gallery would have "alt" attributes like 'First Beef image, gallery 01' and 'Another Beef image, gallery 01'. Meanwhile, the images in another gallery would have "alt" attributes like 'Pork, gallery 02' and 'Pork, gallery 02'.


# How it works

Javascript file tries to group all images presented on the page based on alt attribute. Then it creates required dom elements and attach even handlers. 

# Demo

The demo could be found here [Wine bars in Singapore](https://www.singaporewinedelivery.com/blogs/news/best-wine-bars-in-singapore)