Oneindig scrollen met laadstand Nieuw
Een projectenlijst die bijlaadt zodra je onderaan komt: skeletregels tijdens het laden, een teller via aria-live, role="feed" met aria-busy en per item positie en totaal. De knop Meer projecten laden blijft bestaan voor toetsenbord en als terugval, aan het eind staat een duidelijke eindregel met terug naar boven. Voor projecten, blogs en nieuws met een vaste lijst.
Projecten van Hendriks Bouw
6 van 24
Dit waren alle 24 projecten.
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.
<!-- Oneindig scrollen met laadstand · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="ois">
<div class="ois__kop"><h3 class="ois__t">Projecten van Hendriks Bouw</h3><p class="ois__n" aria-live="polite">6 van 24</p></div>
<div class="ois__lijst" tabindex="0" aria-label="Projecten, scroll voor meer">
<div class="ois__feed" role="feed" aria-busy="false" aria-label="Projecten"></div>
<div class="ois__skel" hidden aria-hidden="true"><div class="ois__sk"><i></i><b></b></div><div class="ois__sk"><i></i><b></b></div></div>
<button class="ois__meer" type="button">Meer projecten laden</button>
<p class="ois__eind" hidden>Dit waren alle 24 projecten. <button class="ois__top" type="button">Terug naar boven</button></p>
</div>
</div>
</div>/* Oneindig scrollen met laadstand · NedDev · eigen werk. */
.ois{position:absolute;inset:0;display:flex;flex-direction:column;font-family:'Switzer',system-ui,sans-serif;color:#0a0a0a;background:#fff;text-align:left}
.ois *{box-sizing:border-box}
.ois__kop{display:flex;align-items:baseline;justify-content:space-between;gap:8px;padding:10px 14px 8px;border-bottom:1px solid rgba(10,10,10,.12)}
.ois__t{margin:0;font-size:14.5px;font-weight:700;letter-spacing:-.01em}
.ois__n{margin:0;font-size:12px;color:#4a4a4a;font-variant-numeric:tabular-nums;white-space:nowrap}
.ois__lijst{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:4px 14px 12px;scrollbar-width:thin;scrollbar-color:#0a0a0a transparent}
.ois__lijst:focus-visible{outline:2px solid #F5480F;outline-offset:-2px}
.ois__it{display:grid;grid-template-columns:52px 1fr auto;gap:10px;align-items:center;padding:7px 0;border-bottom:1px solid rgba(10,10,10,.08);border-radius:4px;animation:oisIn .4s ease both}
.ois__it:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
@keyframes oisIn{from{opacity:0;transform:translateY(6px)}}
.ois__im{width:52px;height:38px;border-radius:4px;object-fit:cover;display:block;background:#e6e3de}
.ois__it h4{margin:0;font-size:13px;font-weight:600;line-height:1.25}
.ois__it p{margin:1px 0 0;font-size:12px;color:#4a4a4a}
.ois__pr{font-size:12.5px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.ois__sk{display:grid;grid-template-columns:52px 1fr;gap:10px;align-items:center;padding:7px 0}
.ois__sk i,.ois__sk b{display:block;border-radius:4px;background:linear-gradient(90deg,#ece9e4 30%,#f6f5f2 50%,#ece9e4 70%) 0 0/300% 100%;animation:oisSk 1.2s linear infinite}
.ois__sk i{height:38px}.ois__sk b{height:12px;width:70%}
@keyframes oisSk{to{background-position:-150% 0}}
.ois__skel[hidden]{display:none}
.ois__meer,.ois__top{min-height:32px;margin-top:10px;padding:0 12px;border:1px solid #0a0a0a;border-radius:4px;background:#fff;color:#0a0a0a;font:600 12.5px/1 'Switzer',system-ui,sans-serif;cursor:pointer}
.ois__meer:hover,.ois__top:hover{background:#0a0a0a;color:#fff}
.ois__meer:focus-visible,.ois__top:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.ois__meer[hidden]{display:none}
.ois__eind{margin:12px 0 0;font-size:12.5px;color:#3a3a3a}
.ois__top{margin:6px 0 0;display:block}
@media (prefers-reduced-motion:reduce){.ois__it{animation:none}.ois__sk i,.ois__sk b{animation:none}}// Oneindig scrollen met laadstand · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w = root.querySelector('.ois'), lijst = w.querySelector('.ois__lijst'), feed = w.querySelector('.ois__feed');
var skel = w.querySelector('.ois__skel'), meer = w.querySelector('.ois__meer'), eind = w.querySelector('.ois__eind'), n = w.querySelector('.ois__n');
var media = (function () { var s = document.createElement('a'); s.href = 'https://neddev.com/lab-media/'; return s.href; })();
var T = [['Badkamer', 'linkvoorbeeld-hover-1'], ['Hybride warmtepomp', 'linkvoorbeeld-hover-2'], ['Praktijkruimte', 'linkvoorbeeld-hover-3'], ['Receptie en wachtkamer', 'snap-onepager-1']];
var P = ['Nijmegen', 'Lent', 'Wijchen', 'Arnhem', 'Elst', 'Beuningen', 'Malden', 'Groesbeek', 'Druten', 'Huissen', 'Bemmel', 'Ubbergen'];
var MND = ['oktober', 'september', 'augustus', 'juli', 'juni', 'mei', 'april', 'maart', 'februari', 'januari'];
var TOT = 24, PER = 6, done = 0, busy = false;
function eur(v) { return '€ ' + String(v).replace(/\B(?=(\d{3})+(?!\d))/g, '.'); }
function add() {
var frag = document.createDocumentFragment();
for (var k = 0; k < PER && done < TOT; k++, done++) {
var t = T[done % T.length], a = document.createElement('article');
a.className = 'ois__it'; a.tabIndex = 0; a.setAttribute('aria-posinset', done + 1); a.setAttribute('aria-setsize', TOT);
var titel = t[0] + ', ' + P[done % P.length];
a.setAttribute('aria-label', titel);
a.innerHTML = '<img class="ois__im" src="' + media + t[1] + '.webp" alt="" width="800" height="436" loading="lazy"><div><h4>' + titel + '</h4><p>Opgeleverd ' + MND[Math.floor(done / 3) % MND.length] + ' 2026</p></div><span class="ois__pr">' + eur(8400 + ((done * 3779) % 31) * 600) + '</span>';
frag.appendChild(a);
}
feed.appendChild(frag);
n.textContent = done + ' van ' + TOT;
}
function load() {
if (busy || done >= TOT) return; busy = true;
feed.setAttribute('aria-busy', 'true'); skel.hidden = false; meer.hidden = true;
setTimeout(function () {
add(); skel.hidden = true; feed.setAttribute('aria-busy', 'false'); busy = false;
if (done >= TOT) { eind.hidden = false; } else { meer.hidden = false; }
}, 850);
}
add();
meer.addEventListener('click', load);
w.querySelector('.ois__top').addEventListener('click', function () { lijst.scrollTop = 0; feed.firstChild.focus({ preventScroll: true }); });
feed.addEventListener('keydown', function (e) {
var it = e.target.closest('.ois__it'); if (!it) return;
if (e.key === 'PageDown' && it.nextElementSibling) { e.preventDefault(); it.nextElementSibling.focus(); }
if (e.key === 'PageUp' && it.previousElementSibling) { e.preventDefault(); it.previousElementSibling.focus(); }
});
if ('IntersectionObserver' in window) new IntersectionObserver(function (e) { if (e[0].isIntersecting) load(); }, { root: lijst, rootMargin: '0px 0px 80px 0px' }).observe(meer);
}
init(document.querySelector('.jouw-container'));
Meer uit scroll-effecten
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 →