javascripthtmlimagegallery

Monitor an images folder to get a live local html/javascript gallery


I need to get a live image gallery based on a permanently monitored folder. It will run on my local computer. Here is how it should work:

Based on some samples founded in Stackoverflow, I've done that. But it doesn't refresh img. And if I well understand the code, it will not remove an image if the file disappear. What do you think? (As you can see I'm not very used with new technologies... I'm an old html/css webmaster)

<!doctype html>
<html lang="fr">
<head>
  <meta charset="utf-8">
  <title>Titre de la page</title>
<link rel="stylesheet" href="style.css">

<script>

function refresh(node)
{
   var times = 1000; // gap in Milli Seconds;

   (function startRefresh()
   {
      var address;
      if(node.src.indexOf('?')>-1)
       address = node.src.split('?')[0];
      else 
       address = node.src;
      node.src = address+"?time="+new Date().getTime();

      setTimeout(startRefresh,times);
   })();

}

window.onload = function()
{
  var node = document.getElementsByClassName('img');
  refresh(node);
  // you can refresh as many images you want just repeat above steps
}

</script>

</head>
<body>

<div class="ajax-inserted-content">
    <section class="section gallery grid layout-4-col">
        <div class="container">
            <div class="row" id='gallery-items'>
            </div>
        </div>
    </section>
</div>
<script>
function doPortItem(n) {
    let img = document.createElement('img');
    img.src = './imgportfolio/test-' + n + '.jpg';
    img.addEventListener('load', () => {
        let div = `<div class="col-3 col-lg-4 col-md-6 col-sm-12">
     <div class="gallery-item">
       <div class="gallery-img">
         <img class="img" data-lazy-image data-src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzIDIiPjwvc3ZnPg==" src="${img.src}" alt="Demo img"> 
       </div>
     </div>
   </div>`;
        document.querySelector('#gallery-items').innerHTML += div;
        doPortItem(++n)
    })
}
doPortItem(1)
</script>

</body>
</html>

Solution

  • In short: you can't read a directory completely client-based because this would be a security nightmare: you could easily crawl a user's file system.

    Workaround - Client-based-only: requiring a predictable naming scheme

    If you really can't run a local server supporting server side scripts like php your only chance is probably to deploy a consistent numeric image file-name scheme and hide non-existent images in the gallery view like img-1 to img-100:

    let dir = 'https://picsum.photos/id';
    let suffix = '/100/100';
    let start = -3;
    let end = 3;
    
    // init
    loadImages(dir);
    
    // reload
    let interval = 60 * 1000;
    setInterval(loadImages, interval);
    
    
    function loadImages(dir) {
      let html = '';
    
      //reset content
      imageGrid.innerHTML = '';
    
      for (let i = start; i < end; i++) {
        let src = `${dir}/${i}${suffix}`;
        html +=
          `<a href="${dir}/${i}${suffix}" data-lg-size="100-100">
             <img onerror="this.parentNode.classList.add('broken-image')" src="${src}">
          </a>`;
      }
      imageGrid.insertAdjacentHTML('beforeend', html);
    
    }
    .imageGrid {
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
      gap: 1em;
    }
    
    img {
      width: 100%;
      aspect-ratio: 1;
      border: 1px solid #ccc;
    }
    
    .broken-image {
      display: none;
    }
       
    
    <h3>3 images are hidden as they don't exist</h3>
    <div id="imageGrid" class="imageGrid"></div>
    This is rather a hack and by no means great. In fact we're assuming we have 6 images named by a predictable index suffix.

    If an image can't be loaded it is hidden via onerror callback function.

    If you have control of the file naming conventions – this is probably the best you can get from 100% client-side approach.

    Updating file list

    The directory-list updates would rely on a recurring method call like setInterval(). You can't get a proper live-update and should probably avoid too many updates – for instance an update each minute (or every 5 minutes) is more reasonable than updating every second.

    Re-initializing gallery scripts

    When updating the gallery you would usually need to initialize the gallery script (binding lightbox events etc.)

    Local server

    I highly recommend to install a local server providing e.g php functionality. For instance via an application like xampp, mampp etc.

    In this case you could write a php script responding to a JS fetch() call like so:

    JavaScript
    We're sending a POST request via fetch() to a php script which return a file list for the specified directory including only certain file types.

    // directory to search for image files
    let dir = 'img';
    
    // allowed file extensions
    let extensions = ['webp', 'png', 'jpg', 'gif'];
    
    // php script url for directory listing
    let phpScan = 'filelist.php';
    
    // update interval: ever minute = 60000ms
    let interval = 60 * 1000;
    
    let fileList;
    
    // init async call
    (async () => {
    
        //init
        fileList = await getFilelist(dir, phpScan, extensions);
        updateGallery(fileList);
    
        setInterval(async () => {
            fileList = await getFilelist(dir, phpScan, extensions);
            updateGallery(fileList)
        }, interval)
    
    })();
    
    
    // dummy update function
    function updateGallery(fileList) {
        console.log(fileList);
    }
    
    /**
     * retrieve file directory info 
     * via php script
     */
    async function getFilelist(dir, phpScan, extensions) {
        let list = await (await fetch(
            phpScan,
            {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    dir: dir,
                    extensions: extensions
                })
            })).json();
        return list;
    }
    

    If you're using a local server you may also use this approach as described by fmacdee: "How to list files inside folder using fetch?".

    This approach takes for granted your local server has directory listing enabled. In other words: the server will automatically generate a HTML table view you can "parse" via fetch result. Here is a modified version:

    
    async function getDirectory(dir, extensions) {
        let response = await fetch(dir);
        let html = await response.text();
        let doc = new DOMParser().parseFromString(html, 'text/html');
        console.log(doc);
    
        let files = [...doc.querySelectorAll("a")].map(a => {
            // filter allowed extenstion
            let ext = a.href.split('.').slice(-1)[0];
            if (extensions.includes(ext)) {
                let filename = a.href.split('/').slice(-1)[0];
                let mod = a.parentNode.nextElementSibling.textContent.trim()
                return { filename: filename, href: a.href, mod: mod }
            }
            return
        }
        ).filter(Boolean)
    
        return files;
    }
    

    All in all: I highly recommend to take the time to install a local server.
    We certainly won't see any new JS features that allow client-side directory listing, as this would have huge security implications.