Let's Automate the Gallery!

Okay so, last time we cleaned up the blog code. This time we're cleaning up the gallery. I do not abide by the idea of eternally adding the same copy pasted div every time I want to add art to the gallery.

Instead, I want JavaScript πŸ’ͺ to do the copy pasting for me and I want to give it as little data as possible because I'm lazy. So here's how I would like to be able to update the gallery:

Storing the Information 🎨

So first lets make a data file. We'll call it something like art.data.js the data just lets me know its a file that only contains information, no actually executing code. In it we'll set up and export four arrays that look like this:

export let colored_art = [
        {
            imgs: ["image1.jpg"],
            desc: "Image description",
        },
    ];

One for colored art, one for black and white art, one for sketches, and lastly one for comics.

Making them arrays allows for us to cleanly iterate over them in the future and use many of the build in JS array methods.

Ok, so the process so far should look something like this:

  1. put the image in the folder for the type of art it is.
  2. add a new entry to the correct art array.
  3. ???
  4. profit.βœ”

Except now we have to write number 3. 😩

Step 3 - Profit βœ”

Ok it's not really that bad, it's the same kind of concept we used in creating our blog's article cards. We're going to take our data, iterate over it, and generate some HTML for each data point we iterate over.

Then, when we've got all the html made we shunt it into the gallery page when the user loads the page or clicks to a new gallery type!

So lets break it down into smaller parts again, it really helps with tackling larger ideas like this.

  1. Generate the HTML
  2. Inject the HTML into the Page
  3. Change the HTML when the user changes galleries.

Generating the HTML πŸ”ƒ

Time for some iteration baby! In fact we need to do a little bit of nested looping. Generally this might be a problem if we were iterating over a large amount of things, double for loops are expensive, but in this situation we're rarely iterating over numbers larger than 10, if ever.

The scenario is that, each gallery post can have more than one image in it. It rarely will but sometimes it might and so for each gallery block we're making, we need to generate all the image tags.

So the function is going to look something like this:

//Import the art data from our data file.
    import {colored_art, bw_art, sketches_art, comics_art} from './art.data.js';


    function generateImageBlocks(artData){
        //1. create an array to hold all our little html snippets we make
        let htmlSnippetList = [];

        //2. Whatever art data this function is passed, we need to iterate over it.
        artData.forEach(art => {

            //2a. grab the arts' urls and set up a var to hold the snippet we make.
            let imageURLs = art.imgs;
            let imgHTMLSnippets = "";

            //2b. lets create the img tag html snippets
            imageURLs.forEach( url => {
                let imgSnippet = `<a href="${url}"><img class="art" src="${url}" alt="">
                </a>`;
                //we append each img tag one after another in a big string.
                imgHTMLSnippet += imgSnippet;
            });

            //2c. now we can create the full div block using the img tags we just made.
            let imgBlockSnippet = `<div class="art-frame">${imgHTMLSnippets} <br/>
            ${art.desc}</div>`;

            //push this snippet onto the arry of snippets and the loop moves on to the
            //next art data entry.
            htmlSnippetList.push(imgBlockSnippet);
        });

        //3. after all that we return all the snippets we made from to use later on.
        return htmlSnippetList;
    }

Whew πŸ’¦ ok. This is the hardest part done! We now have an array of little <div> snippets we can inject into a webpage. Now we just need to write a much smaller function that handles that for us.

Injecting the HTML πŸ’‰

This is much easier, we just need to grab the <div> we're going to shove all these snippets into, and do so! Let's be sure to label the div something like <div id="gal-content"></div> that way we can easily query it in JS.

function loadArtBlocks(htmlSnippetArray){

        let contentContainer = document.querySelector('#gal-content');

        htmlSnippetArray.forEach(artBlock => {
            contentContainer.innerHTML += artBlock;
        });
    }

Done! Though...in the future this could absolutely be improved. We loop over the divs here to add them, and looping always costs time.

Perhaps later we can instead build a string of these div blocks instead of an array and then just append the entire long html string to the contentContainer. That would be something we do the generateImageBlocks() function.

And just real quick, now that we can load the blocks, we should probably make a little function that clears them too.

function clearBlocks(){

        let contentContainer = document.querySelector('#gal-content');
        contentContainer.innerHTML = '';
    }

There! Now we can add and remove the gallery html whenever we call one of these two functions.

Putting It All Together πŸ› οΈ

Ok so, we have all the pieces in our three functions and our data file:
generateImageBlocks(artData), loadArtBlocks(), clearBlocks()

Now we need to put them to use. So first things first, when a user clicked the gallery button from the home page, let's show them the colored gallery first.

//generate our html
    let colorArtBlocks = generateImageBlocks(colored_art);

    //when the window loads, inject that HTML we just made into the page.
    window.onload = function(){
        loadArtBlocks(colorArtBlocks);
    }

Perfect! Now when you load the gallery the colored images all load! But...we also need to be able to change the gallery with the nav buttons at the bottom of the page. So lets modify our window.onload event to also add some click events to those.

window.onload = function(){
        document.getElementById("colored").addEventListener("click", ()=> { swapGallery("colored-gal")});
        document.getElementById("bw").addEventListener("click", ()=> { swapGallery("bw-gal")});
        document.getElementById("sketches").addEventListener("click", ()=> { swapGallery("sketches-gal")});
        document.getElementById("comics").addEventListener("click", ()=> { swapGallery("comics-gal")});
    }

There, now when we click each button they'll...run a function we haven't made yet!! swapGallery(switchKey) is a function we'll make that will do the following:

With this last function, our entire process will be complete, so lets take a look at it!

    //lets keep in mind what gallery is currently loaded.
        let activeGallery = "colored-gal";

        function swapGallery(switchKey){

            //1. if they click on a gallery already loaded we don't try to load it again.
            if(switchKey  activeGallery){
                return;
            }
            //1a. set the gallery to the one they just clicked.
            activeGallery = switchKey;

            //2. check which button they clicked and preform the correct galley swap.
            switch(switchKey){

            case "colored-gal":
                clearBlocked(); //clear out the old gallery
                let colored = generateImageBlocks(colored_art); //make the new gallery
                loadArtBlocks(colored); //inject them into the page.
                break; //exit the switch statement!
            }
            case "bw-gal":
                clearBlocked(); //clear out the old gallery
                let bw = generateImageBlocks(bw_art); //make the new gallery
                loadArtBlocks(bw); //inject them into the page.
                break; //exit the switch statement!
            }
            case "sketches-gal":
                clearBlocked(); //clear out the old gallery
                let sketchs = generateImageBlocks(sketches_art); //make the new gallery
                loadArtBlocks(sketchs); //inject them into the page.
                break; //exit the switch statement!
            }
            case "comics-gal":
                clearBlocked(); //clear out the old gallery
                let comics = generateImageBlocks(comics_art); //make the new gallery
                loadArtBlocks(comics); //inject them into the page.
                break; //exit the switch statement!
            }
        }

✨That's it! With that last function assigned to our buttons the gallery now works with out any tedious copy pasting of html, or fussing with changing things. We drop the image into a folder and add it's data to a file!

Conclusions πŸ€”

So, this works very well, but has some rough edges that should be cleaned up in the future. The initial load isn't too slow now, (for neocities), but it will eventually be too much as we add art to the gallery.

There isn't a good method of preloading these images, so the solution in the future will likely be to paginate each gallery type, so that we aren't loading a huge batch of images at once.

There are also some code inefficiencies in this first iteration that can be cleaned up, as mentioned earlier about the second for loop.

But all in all, I mark this a success! Refactoring for ease of use has been going along at a good pace. The goal ultimately is to make sure it is as easy as possible for me to add need content so that my ability to keep sundered space updated never becomes too tedious!