Skip to main content

Allowing hyperlinks in image gallery

in an Html block put:

For 1 gallery 

<script>
(function () {
  var OPEN_IN_NEW_TAB = true;

  function extractUrl(text) {
    if (!text) return null;
    var match = text.match(/https?:\/\/[^\s)]+/i);
    return match ? match[0] : null;
  }

  function sanitizeCaption(captionEl, url) {
    var txt = captionEl.textContent || '';
    var cleaned = txt.replace(url, '').replace(/\s{2,}/g, ' ').trim();
    captionEl.textContent = cleaned;
  }

  function attachClickBehavior(wrapper, url) {
    if (!url || wrapper.dataset.linkified === '1') return;

    wrapper.dataset.linkified = '1';
    wrapper.dataset.href = url;
    wrapper.style.cursor = 'pointer';

    // Prevent click during drag/swipe
    var isDragging = false, startX = 0, startY = 0;
    wrapper.addEventListener('mousedown', e => { startX = e.clientX; startY = e.clientY; isDragging = false; }, true);
    wrapper.addEventListener('mousemove', e => {
      if (Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5) isDragging = true;
    }, true);

    wrapper.addEventListener('click', e => {
      if (isDragging || e.target.closest('a')) return;
      if (OPEN_IN_NEW_TAB) window.open(url, '_blank', 'noopener');
      else window.location.href = url;
    }, true);
  }

  function processSlide(slide) {
    if (!slide || slide.dataset.processed === '1') return;
    slide.dataset.processed = '1';

    var img = slide.querySelector('img');
    var caption = slide.querySelector('.caption');
    if (!img || !caption) return;

    var url = extractUrl(caption.textContent);
    if (!url) return;
    sanitizeCaption(caption, url);

    var wrapper = slide.querySelector('div[style*="display: inline-block"]') || slide;
    attachClickBehavior(wrapper, url);

    // Accessibility handling
    function updateAccessibility() {
      const isHidden = slide.getAttribute('aria-hidden') === 'true';
      if (isHidden) {
        wrapper.removeAttribute('role');
        wrapper.setAttribute('tabindex', '-1');
      } else {
        wrapper.setAttribute('role', 'link');
        wrapper.setAttribute('tabindex', '0');
      }
    }
    updateAccessibility();

    // Observe aria-hidden changes (Slick updates this on slide change)
    const obs = new MutationObserver(updateAccessibility);
    obs.observe(slide, { attributes: true, attributeFilter: ['aria-hidden'] });
  }

  function init() {
    const track = document.querySelector('.slick-track');
    if (!track) return setTimeout(init, 500);
    track.querySelectorAll('.slick-slide').forEach(processSlide);

    // Re-run when Slick adds/clones slides
    const obs = new MutationObserver(m => {
      m.forEach(r => {
        r.addedNodes && r.addedNodes.forEach(n => {
          if (n.nodeType === 1 && n.classList.contains('slick-slide')) processSlide(n);
        });
      });
    });
    obs.observe(track, { childList: true, subtree: true });
  }

  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
  else init();
})();
</script>

for numerous galleries 

<script>
(function () {
  var OPEN_IN_NEW_TAB = true;

  function extractUrl(text) {
    if (!text) return null;
    var match = text.match(/https?:\/\/[^\s)]+/i);
    return match ? match[0] : null;
  }

  function sanitizeCaption(captionEl, url) {
    var txt = captionEl.textContent || '';
    var cleaned = txt.replace(url, '').replace(/\s{2,}/g, ' ').trim();
    captionEl.textContent = cleaned;
  }

  function attachClickBehavior(wrapper, url) {
    if (!url || wrapper.dataset.linkified === '1') return;

    wrapper.dataset.linkified = '1';
    wrapper.dataset.href = url;
    wrapper.style.cursor = 'pointer';

    // Prevent click during drag/swipe
    var isDragging = false, startX = 0, startY = 0;
    wrapper.addEventListener('mousedown', function (e) {
      startX = e.clientX; startY = e.clientY; isDragging = false;
    }, true);
    wrapper.addEventListener('mousemove', function (e) {
      if (Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5) isDragging = true;
    }, true);

    wrapper.addEventListener('click', function (e) {
      if (isDragging || e.target.closest('a')) return;
      if (OPEN_IN_NEW_TAB) window.open(url, '_blank', 'noopener');
      else window.location.href = url;
    }, true);
  }

  function processSlide(slide) {
    if (!slide || slide.dataset.processed === '1') return;
    slide.dataset.processed = '1';

    var img = slide.querySelector('img');
    var caption = slide.querySelector('.caption');
    if (!img || !caption) return;

    var url = extractUrl(caption.textContent);
    if (!url) return;
    sanitizeCaption(caption, url);

    var wrapper = slide.querySelector('div[style*="display: inline-block"]') || slide;
    attachClickBehavior(wrapper, url);

    // Accessibility handling
    function updateAccessibility() {
      var isHidden = slide.getAttribute('aria-hidden') === 'true';
      if (isHidden) {
        wrapper.removeAttribute('role');
        wrapper.setAttribute('tabindex', '-1');
      } else {
        wrapper.setAttribute('role', 'link');
        wrapper.setAttribute('tabindex', '0');
      }
    }
    updateAccessibility();

    // Observe aria-hidden changes (Slick updates this on slide change)
    var obs = new MutationObserver(updateAccessibility);
    obs.observe(slide, { attributes: true, attributeFilter: ['aria-hidden'] });
  }

  // Initialize one track (one gallery)
  function initTrack(track) {
    if (!track || track.dataset.linkifyInit === '1') return;
    track.dataset.linkifyInit = '1';

    track.querySelectorAll('.slick-slide').forEach(processSlide);

    // Re-run when Slick adds/clones slides inside this track
    var obs = new MutationObserver(function (mutations) {
      mutations.forEach(function (r) {
        if (r.addedNodes) {
          r.addedNodes.forEach(function (n) {
            if (n.nodeType === 1 && n.classList.contains('slick-slide')) processSlide(n);
            // In case a subtree with multiple slides was injected
            if (n.nodeType === 1) n.querySelectorAll && n.querySelectorAll('.slick-slide').forEach(processSlide);
          });
        }
      });
    });
    obs.observe(track, { childList: true, subtree: true });
  }

  // Initialize all current tracks; keep trying until at least one exists
  function initAll() {
    var tracks = document.querySelectorAll('.slick-track');
    if (!tracks.length) return setTimeout(initAll, 500);
    tracks.forEach(initTrack);
  }

  // Also watch for galleries added later (AJAX, route changes, etc.)
  function watchForNewTracks() {
    var rootObs = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        m.addedNodes && m.addedNodes.forEach(function (n) {
          if (n.nodeType !== 1) return;
          if (n.matches && n.matches('.slick-track')) initTrack(n);
          if (n.querySelectorAll) n.querySelectorAll('.slick-track').forEach(initTrack);
        });
      });
    });
    rootObs.observe(document.documentElement, { childList: true, subtree: true });
  }

  function start() {
    initAll();
    watchForNewTracks();
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', start);
  } else {
    start();
  }
})();
</script>

 

 

then the images can have something similar to: 

An image gallery with two items: a gradient with a link to Google, and a photo collage with a link to Getstreamline.