Allowing hyperlinks in image gallery
in an Html block put:
<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>
<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:
