How to add a button to the Photo Gallery Popup to download images

Hello,

Here you have the option to make images used in the Photo Gallery available for download. I find this feature to be the default setting for the Photo Gallery. Unfortunately, nothing has changed at Elfsight since I first requested it in 2022 – which is a shame. I’ve spent several hours trying, and here’s the code that should be added to the Custom JS field on the Style tab of your widget’s settings:

(function () {
  /** 1) URL aus url("…") extrahieren */
  function extractUrl(str) {
    const m = /url\(["']?(.*?)["']?\)/.exec(str || '');
    return m ? m[1] : null;
  }

  /** 2) Aktives Slide finden und Bild-URL zurückgeben */
  function getCurrentImageUrl() {
    const holders = document.querySelectorAll('.fslightbox-source-holder');
    for (const h of holders) {
      if (h.style.transform.includes('translate(0px')) {
        // echtes <img> suchen
        const img = h.querySelector('img');
        if (img && img.src) return img.src;
        // fallback: CSS-background
        const wrapper = h.querySelector('.fslightbox-single-source-wrapper');
        if (wrapper) {
          const bg = getComputedStyle(wrapper).backgroundImage;
          return extractUrl(bg);
        }
      }
    }
    return null;
  }

  /** 3) Download per fetch→Blob starten */
  async function triggerDownload(url) {
    try {
      const resp = await fetch(url, { mode: 'cors' });
      if (!resp.ok) throw new Error('Status ' + resp.status);
      const blob = await resp.blob();
      const blobUrl = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = blobUrl;
      const parts = url.split('/');
      a.download = parts[parts.length - 1] || 'download.jpg';
      document.body.appendChild(a);
      a.click();
      a.remove();
      setTimeout(() => URL.revokeObjectURL(blobUrl), 60000);
    } catch (e) {
      console.error('Download-Fehler:', e);
      window.open(url, '_blank');
    }
  }

  /** 4) Button einmalig in die Toolbar einfügen */
  function injectButton() {
    if (document.getElementById('fs-dl-btn')) return;
    const toolbar = document.querySelector('.fslightbox-toolbar');
    if (!toolbar) return;

    const btn = document.createElement('div');
    btn.id = 'fs-dl-btn';
    btn.className = 'fslightbox-toolbar-button button-style';
    btn.title = 'Bild herunterladen';
    btn.innerHTML = `
      <svg class="fslightbox-svg-icon" viewBox="0 0 24 24" width="17" height="17">
        <!-- Pfeil nach unten -->
        <path d="M12 20l-6-6h4V4h4v10h4l-6 6z"/>
      </svg>
    `;

    btn.addEventListener('click', async () => {
      const url = getCurrentImageUrl();
      if (url) await triggerDownload(url);
      else console.warn('❌ Keine Bild-URL gefunden');
    });

    toolbar.insertBefore(btn, toolbar.firstChild);
  }

  /** 5) Button-State (sichtbar / ausgegraut) */
  function updateButtonState() {
    const btn = document.getElementById('fs-dl-btn');
    if (!btn) return;
    btn.style.opacity = getCurrentImageUrl() ? '1' : '0.3';
  }

  /** 6) Initialisierung + Observer + Fallback */
  injectButton();
  updateButtonState();

  const mo = new MutationObserver(() => {
    injectButton();
    updateButtonState();
  });
  mo.observe(document.body, { childList: true, subtree: true });

  setInterval(() => {
    injectButton();
    updateButtonState();
  }, 500);
})();

BG, Timo

1 Like

Hey there, @Fri :waving_hand:

We’d love to release the new features faster, but given the high number of requests, we have to prioritize based on their complexity and number votes.

At the moment, we have a bunch of requests with the higher priority level and our devs focused on them. If your request gets more votes, it might be prioritized sooner, but it’s hard to give any predictions for now. If any news comes up, we’ll update you on the Wishlist thread :slightly_smiling_face:

However, that’s truly amazing that you’ve found a custom solution that works like a charm. A huge thanks for sharing it with us - that’s much appreciated :heart:

1 Like