Productgalerij met miniaturen Nieuw
De fotogalerij van een productpagina: groot beeld dat je veegt (scroll-snap, op desktop ook slepen met de muis), miniaturen ernaast die meelopen en de actieve foto tonen, pijlen en een teller. Pijltjestoetsen werken op het beeld, de miniaturen zijn gewone knoppen. Zonder JS blijft het een veegbare rij foto's. Voor elke webshop met meerdere productfoto's.





Zo gebruik je het
- Plak de HTML op de plek waar het onderdeel moet komen.
- Zet de CSS in je stylesheet. Alle klassen hebben een eigen voorvoegsel, dus niets botst met je eigen opmaak.
- Voeg de JavaScript toe en roep init() aan met het element waar de HTML in staat.
<!-- Productgalerij met miniaturen · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="pgm">
<div class="pgm__thumbs" aria-label="Kies een foto">
<button class="pgm__th" type="button" data-i="0" aria-label="Foto 1: voorkant" aria-current="true"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-1.webp" alt="" width="900" height="491" loading="lazy"></button>
<button class="pgm__th" type="button" data-i="1" aria-label="Foto 2: zijkant"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-2.webp" alt="" width="900" height="491" loading="lazy"></button>
<button class="pgm__th" type="button" data-i="2" aria-label="Foto 3: gesp en stiksels"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-3.webp" alt="" width="900" height="491" loading="lazy"></button>
<button class="pgm__th" type="button" data-i="3" aria-label="Foto 4: binnenkant met laptopvak"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-4.webp" alt="" width="900" height="491" loading="lazy"></button>
<button class="pgm__th" type="button" data-i="4" aria-label="Foto 5: gedragen in Utrecht"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-5.webp" alt="" width="900" height="491" loading="lazy"></button>
</div>
<div class="pgm__main">
<div class="pgm__track" tabindex="0" role="region" aria-roledescription="galerij" aria-label="Foto's van rugzak Oudegracht, veeg of gebruik de pijltjestoetsen">
<figure class="pgm__sl"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-1.webp" alt="Cognac leren rugzak, voorkant" width="900" height="491" draggable="false"></figure>
<figure class="pgm__sl"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-2.webp" alt="Rugzak van opzij, met zijvak" width="900" height="491" loading="lazy" draggable="false"></figure>
<figure class="pgm__sl"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-3.webp" alt="Messing gesp en stiksels van dichtbij" width="900" height="491" loading="lazy" draggable="false"></figure>
<figure class="pgm__sl"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-4.webp" alt="Open rugzak van boven met laptopvak" width="900" height="491" loading="lazy" draggable="false"></figure>
<figure class="pgm__sl"><img src="https://neddev.com/lab-media/productgalerij-miniaturen-5.webp" alt="Iemand loopt met de rugzak over een gracht in Utrecht" width="900" height="491" loading="lazy" draggable="false"></figure>
</div>
<button class="pgm__nav pgm__prev" type="button" aria-label="Vorige foto"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 6l-6 6 6 6" fill="none" stroke="currentColor" stroke-width="2"/></svg></button>
<button class="pgm__nav pgm__next" type="button" aria-label="Volgende foto"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9.5 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2"/></svg></button>
<span class="pgm__cnt" aria-live="polite">1 / 5</span>
</div>
</div>
</div>/* Productgalerij met miniaturen · NedDev · eigen werk. */
.pgm{position:absolute;inset:10px;display:grid;grid-template-columns:44px 1fr;gap:8px;font-family:'Switzer',sans-serif;container-type:size}
.pgm__thumbs{display:flex;flex-direction:column;gap:5px;justify-content:center}
.pgm__th{position:relative;width:44px;height:calc((100cqh - 20px) / 5);max-height:44px;min-height:28px;padding:0;border:0;border-radius:4px;overflow:hidden;background:#ddd;cursor:pointer;opacity:.6;transition:opacity .2s}
.pgm__th img{display:block;width:100%;height:100%;object-fit:cover}
.pgm__th:hover{opacity:.9}
.pgm__th[aria-current="true"]{opacity:1;box-shadow:inset 0 0 0 2px #F5480F}
.pgm__th[aria-current="true"]::after{content:"";position:absolute;inset:0;border:2px solid #F5480F;border-radius:4px}
.pgm__th:focus-visible{outline:2px solid #0a0a0a;outline-offset:2px}
.pgm__main{position:relative;min-width:0;border-radius:8px;overflow:hidden;background:#d4d4d4}
.pgm__track{display:flex;width:100%;height:100%;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scroll-behavior:smooth;scrollbar-width:none;overscroll-behavior-x:contain;outline:none;cursor:grab}
.pgm__track::-webkit-scrollbar{display:none}
.pgm__track.is-drag{scroll-snap-type:none;scroll-behavior:auto;cursor:grabbing}
.pgm__track:focus-visible{box-shadow:inset 0 0 0 2px #F5480F}
.pgm__sl{flex:0 0 100%;height:100%;margin:0;scroll-snap-align:start}
.pgm__sl img{display:block;width:100%;height:100%;object-fit:cover;user-select:none;-webkit-user-drag:none;pointer-events:none}
.pgm__nav{position:absolute;top:50%;width:32px;height:32px;margin-top:-16px;border:0;border-radius:4px;background:#fff;color:#0a0a0a;display:grid;place-items:center;cursor:pointer;box-shadow:0 2px 8px rgba(10,10,10,.15)}
.pgm__nav svg{width:18px;height:18px}
.pgm__prev{left:8px}.pgm__next{right:8px}
.pgm__nav:disabled{opacity:0;pointer-events:none}
.pgm__nav:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.pgm__cnt{position:absolute;right:8px;bottom:8px;padding:4px 7px;border-radius:4px;background:rgba(10,10,10,.72);color:#fff;font:500 12px/1 'Switzer',sans-serif;font-variant-numeric:tabular-nums}
@media (prefers-reduced-motion:reduce){.pgm__track{scroll-behavior:auto}.pgm__th{transition:none}}// Productgalerij met miniaturen · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.pgm');if(!w)return;
var tr=w.querySelector('.pgm__track'),ths=[].slice.call(w.querySelectorAll('.pgm__th')),prev=w.querySelector('.pgm__prev'),next=w.querySelector('.pgm__next'),cnt=w.querySelector('.pgm__cnt'),N=ths.length,cur=0;
var rm=matchMedia('(prefers-reduced-motion: reduce)').matches;
function go(i){i=Math.max(0,Math.min(N-1,i));tr.scrollTo({left:i*tr.clientWidth,behavior:rm?'auto':'smooth'});}
function upd(){var i=Math.round(tr.scrollLeft/Math.max(1,tr.clientWidth));if(i===cur&&cnt.textContent)return set(i);set(i);}
function set(i){cur=i;ths.forEach(function(t,k){if(k===i)t.setAttribute('aria-current','true');else t.removeAttribute('aria-current');});prev.disabled=i===0;next.disabled=i===N-1;cnt.textContent=(i+1)+' / '+N;}
var tk=false;tr.addEventListener('scroll',function(){if(tk)return;tk=true;requestAnimationFrame(function(){tk=false;upd();});},{passive:true});
ths.forEach(function(t,i){t.addEventListener('click',function(){go(i);});});
prev.addEventListener('click',function(){go(cur-1);});next.addEventListener('click',function(){go(cur+1);});
tr.addEventListener('keydown',function(e){if(e.key==='ArrowRight'){e.preventDefault();go(cur+1);}else if(e.key==='ArrowLeft'){e.preventDefault();go(cur-1);}else if(e.key==='Home'){e.preventDefault();go(0);}else if(e.key==='End'){e.preventDefault();go(N-1);}});
/* muis-slepen op desktop; aanraken gebruikt gewoon de native swipe */
var dn=false,sx=0,sl=0,moved=0;
tr.addEventListener('pointerdown',function(e){if(e.pointerType!=='mouse'||e.button!==0)return;dn=true;moved=0;sx=e.clientX;sl=tr.scrollLeft;tr.classList.add('is-drag');try{tr.setPointerCapture(e.pointerId);}catch(_){}});
tr.addEventListener('pointermove',function(e){if(!dn)return;var dx=e.clientX-sx;moved=Math.abs(dx);tr.scrollLeft=sl-dx;});
function up(e){if(!dn)return;dn=false;tr.classList.remove('is-drag');var dx=e.clientX-sx,i=Math.round(sl/tr.clientWidth);if(dx<-40)i++;else if(dx>40)i--;go(i);}
tr.addEventListener('pointerup',up);tr.addEventListener('pointercancel',up);
if('ResizeObserver' in window)new ResizeObserver(function(){tr.scrollLeft=cur*tr.clientWidth;}).observe(tr);
set(0);
}
init(document.querySelector('.jouw-container'));
Meer uit componenten
Iets gezien dat je mooi vindt?
Zet het in je selectie en stuur hem op. Dan bouwen we je site eromheen, in jouw huisstijl.
Neem contact op →