Rework lazy loading as js controlled fetches

This commit is contained in:
2026-01-28 13:20:35 +01:00
parent 80e3aa95d8
commit e6d6ad3c7b
2 changed files with 95 additions and 11 deletions

View File

@@ -8,15 +8,101 @@
// ============================================================
// Gallery Thumbnails Module
// ============================================================
// Thumbnail loading now relies on native loading="lazy" attribute.
// The browser handles deferred loading, connection limits, and
// automatic cancellation on navigation.
//
// This module is kept as a stub for potential future enhancements.
// Uses fetch() with AbortController to load thumbnails.
// This allows true HTTP request cancellation on navigation,
// unlike native loading="lazy" where queued requests block.
Luxtools.GalleryThumbnails = (function () {
var controller = null;
var maxConcurrent = 4;
var activeCount = 0;
var queue = [];
function abortAll() {
if (controller) {
controller.abort();
controller = null;
}
queue = [];
activeCount = 0;
}
function processQueue() {
if (!controller) return;
while (activeCount < maxConcurrent && queue.length > 0) {
var img = queue.shift();
loadThumb(img);
}
}
function loadThumb(img) {
if (!controller) return;
var src = img.getAttribute('data-src');
if (!src) {
processQueue();
return;
}
activeCount++;
fetch(src, { signal: controller.signal })
.then(function (response) {
if (!response.ok) throw new Error('HTTP ' + response.status);
return response.blob();
})
.then(function (blob) {
img.src = URL.createObjectURL(blob);
img.removeAttribute('data-src');
})
.catch(function (err) {
// Aborted or failed - ignore
if (err.name !== 'AbortError') {
// Keep data-src for potential retry, just log
}
})
.finally(function () {
activeCount--;
processQueue();
});
}
function queueThumb(img) {
if (!controller) return;
if (!img.getAttribute('data-src')) return;
if (img.getAttribute('data-queued') === '1') return;
img.setAttribute('data-queued', '1');
queue.push(img);
processQueue();
}
function init() {
// Native lazy loading handles everything.
// No JavaScript intervention needed.
var imgs = document.querySelectorAll(
'div.luxtools-gallery img.luxtools-thumb[data-src], div.luxtools-imagebox img[data-src]'
);
if (!imgs || !imgs.length) return;
// Create abort controller for all requests
controller = new AbortController();
// Abort all pending requests on navigation
window.addEventListener('beforeunload', abortAll);
window.addEventListener('pagehide', abortAll);
// Use IntersectionObserver to trigger loading
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
queueThumb(entry.target);
io.unobserve(entry.target);
});
}, { rootMargin: '200px' });
imgs.forEach(function (img) { io.observe(img); });
} else {
// Fallback: queue all
imgs.forEach(queueThumb);
}
}
return {