< back

bird wrongs

resources: diy easy photo gallery.

In these tutorials you're going to see me making a lot of JSON files, because they're kind of the poor programmer's database—or I guess to be more accurate they have more in common with NoSQL databases, but that's splitting hairs. The point is that, yes, once again I am going to tell you to make a JSON file with your photo data. Mine looks kind of like this:

[
  {
    "file": "/photos/IMG_1331.jpg",
    "caption": "total eclipse 2024: the corona of the sun",
    "date": "2024-04-08"
  },
  {
    "file": "/photos/IMG_1328.jpg",
    "caption": "peaceful river vista in Indiana",
    "date": "2024-04-07"
  },
  {
    "file": "/photos/IMG_1317.jpeg",
    "caption": "caught drinking out of the watering can...",
    "date": "2024-03-31"
  }
]

Pretty self-explanatory; here's three photos, each with a file path, a caption, and a date. I'm just entering newest photos at the top, so I don't really need to do any sorting.

Then, I'm going to do a little setup:

let loadedCount = 0;
const batchSize = 24;
const container = document.querySelector("main");
let photos = [];

I want to keep track of how many photos I've loaded, how many I want to load at a time, where I want to put them (you can use any selector; I just like main), and then an array to keep track of the photos.

Now I'm going to write a function to add a set of photos to the page.

const addPhotos = () => {
  const photoSlice = photos.slice(loadedCount, batchSize);
  let content = "";
  photoSlice.forEach((item) => {
    content += `
      <article class="photo">
        <a href="#"><img src="${item.file}" alt="${item.date} - ${item.caption}" title="${item.date} - ${item.caption}" /></a>
      </article>
    `;
  });
  container.innerHTML += content;
  loadedCount += batchSize;
}

This gets a slice of the size we specified above from the photos array, starting from the point we specified, and then puts each photo into a template that we add to a string of HTML. Then we put the HTML in the document, and increment our loadedCount so we know where to start next time.

const initPhotos = async () => {
  const photoData = await fetch("photos.json");
  photos = await photoData.json();
        
  addPhotos();
        
  document.querySelector("main").addEventListener('load', () => {
    observer = new IntersectionObserver(() => {
      addPhotos();
    });
    observer.observe(document.querySelector("footer"));
  });
}

initPhotos();

Then what I do is create an initializer function that fetches the content of the JSON file and reads it into the photos array, and adds the first batch of photos to the page.

The other thing this function initializes is something called an IntersectionObserver that tracks when an element enters or exits the viewport (that is, the area of the page visible to whoever's looking at it). If we have more than our batch size of photos, whenever the footer element enters the viewport, we load some more photos until we're done. This helps decrease initial load time, which is handy for mobile devices and people on slow connections.

Then, last but not least, we call the initPhotos function to run all of this!

Of course, the photo gallery on this site has a little more scripting on it to power the modals, but I'll get to that in another tutorial.