Zoeker met live teller Nieuw
Locatiezoeker met postcode, soort-chips en een straal-schuif; de zoekknop telt live mee ("Toon 23 locaties") en bij nul resultaten biedt hij één uitweg (straal vergroten). Postcode wordt geformatteerd en gevalideerd (1234 AB). Demo-data in de JS; in productie haal je de coördinaat van de postcode uit PDOK Locatieserver (gratis) en de locaties uit je eigen API.
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.
<!-- Zoeker met live teller · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="zlt"><div class="zlt__in">
<form class="zlt__card" role="search" aria-label="Kinderopvang zoeken" novalidate>
<h3 class="zlt__h">Kinderopvang bij jou in de buurt</h3>
<div class="zlt__grid">
<div class="zlt__f zlt__f--pc"><label class="zlt__l" for="zlt-pc">Postcode</label><input class="zlt__i" id="zlt-pc" value="2514 AB" autocomplete="postal-code" maxlength="7" spellcheck="false" aria-describedby="zlt-pe"><p class="zlt__e" id="zlt-pe"></p></div>
<fieldset class="zlt__fs"><legend class="zlt__l">Soort opvang</legend><div class="zlt__chips"><button type="button" class="zlt__chip" aria-pressed="true" data-t="kdv">Dagopvang</button><button type="button" class="zlt__chip" aria-pressed="false" data-t="bso">BSO</button><button type="button" class="zlt__chip" aria-pressed="false" data-t="peuter">Peuteropvang</button><button type="button" class="zlt__chip" aria-pressed="false" data-t="gast">Gastouder</button></div></fieldset>
<div class="zlt__f zlt__f--r"><label class="zlt__l" for="zlt-r">Straal <output class="zlt__ro" for="zlt-r">5 km</output></label><input class="zlt__range" id="zlt-r" type="range" min="1" max="25" step="1" value="5"></div>
</div>
<div class="zlt__act"><button class="zlt__go" type="submit"><span class="zlt__gt">Toon locaties</span></button><button type="button" class="zlt__wider" hidden></button></div>
<p class="zlt__sr" aria-live="polite"></p>
</form>
<div class="zlt__res" tabindex="-1" hidden><p class="zlt__rh"></p><ol class="zlt__list"></ol></div>
</div></div>
</div>/* Zoeker met live teller · NedDev · eigen werk. */
.zlt [hidden]{display:none!important}
.zlt{position:absolute;inset:0;overflow:auto;display:flex;padding:12px;box-sizing:border-box;background:#f4f3f1;font-family:'Switzer',system-ui,sans-serif;color:#0a0a0a;container-type:inline-size;scrollbar-width:thin}
.zlt *,.zlt *::before,.zlt *::after{box-sizing:border-box}
.zlt__in{margin:auto;width:100%;max-width:820px}
.zlt__card{background:#fff;border:1px solid rgba(10,10,10,.12);border-radius:6px;padding:12px 14px}
.zlt__h{margin:0 0 8px;font-size:15.5px;font-weight:700;letter-spacing:-.01em}
.zlt__grid{display:grid;gap:8px 14px;grid-template-columns:110px 1fr}
.zlt__f--pc{grid-column:1}.zlt__f--r{grid-column:2;grid-row:1}.zlt__fs{grid-column:1/-1}
@container (min-width:700px){.zlt__grid{grid-template-columns:120px auto 1fr}.zlt__fs{grid-column:2;grid-row:1}.zlt__f--r{grid-column:3}}
.zlt__l{display:flex;justify-content:space-between;align-items:baseline;font-size:12px;font-weight:600;margin-bottom:4px;padding:0}
.zlt__ro{font:600 11.5px 'JetBrains Mono',monospace;color:#0a0a0a}
.zlt__i{display:block;width:100%;font:600 14px 'JetBrains Mono',monospace;letter-spacing:.04em;text-transform:uppercase;color:#0a0a0a;border:1px solid #8c8c8c;border-radius:4px;padding:7px 9px;background:#fff;transition:border-color .15s,box-shadow .15s}
.zlt__i:hover{border-color:#0a0a0a}
.zlt__i:focus-visible{outline:none;border-color:#0a0a0a;box-shadow:0 0 0 3px rgba(245,72,15,.35)}
.zlt__i[aria-invalid="true"]{border-color:#B42318}
.zlt__e{margin:3px 0 0;font-size:11.5px;color:#B42318;line-height:1.3}.zlt__e:empty{display:none}
.zlt__fs{border:0;margin:0;padding:0;min-width:0}
.zlt__chips{display:flex;flex-wrap:wrap;gap:5px}
.zlt__chip{font:500 12.5px 'Switzer',system-ui,sans-serif;padding:5px 11px;border:1px solid #8c8c8c;border-radius:100px;background:#fff;color:#0a0a0a;cursor:pointer;transition:background .15s,border-color .15s,color .15s}
.zlt__chip:hover{border-color:#0a0a0a}
.zlt__chip[aria-pressed="true"]{background:#0a0a0a;border-color:#0a0a0a;color:#fff}
.zlt__chip:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.zlt__range{width:100%;margin:8px 0 0;height:4px;appearance:none;-webkit-appearance:none;border-radius:2px;background:linear-gradient(#0a0a0a,#0a0a0a) 0/var(--p,17%) 100% no-repeat,#dedad4;cursor:pointer}
.zlt__range::-webkit-slider-thumb{-webkit-appearance:none;width:18px;height:18px;border-radius:50%;background:#fff;border:2px solid #0a0a0a;transition:transform .15s}
.zlt__range::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background:#fff;border:2px solid #0a0a0a}
.zlt__range:hover::-webkit-slider-thumb{transform:scale(1.1)}
.zlt__range:focus-visible{outline:2px solid #F5480F;outline-offset:6px}
.zlt__act{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;margin-top:10px}
.zlt__go{font:600 14px 'Switzer',system-ui,sans-serif;padding:9px 18px;border-radius:4px;border:1px solid #F5480F;background:#F5480F;color:#0a0a0a;cursor:pointer;transition:background .15s,border-color .15s,color .15s}
.zlt__go:hover:not([aria-disabled="true"]){background:#0a0a0a;border-color:#0a0a0a;color:#fff}
.zlt__go[aria-disabled="true"]{background:#e4e2de;border-color:#e4e2de;color:#4f4f4f;cursor:not-allowed}
.zlt__go b{font-variant-numeric:tabular-nums}
.zlt__go.is-tick b{animation:zlt-tick .25s ease-out}
@keyframes zlt-tick{from{opacity:.3}}
.zlt__go:focus-visible,.zlt__wider:focus-visible{outline:2px solid #0a0a0a;outline-offset:2px}
.zlt__wider{font:600 12.5px 'Switzer',system-ui,sans-serif;background:none;border:0;color:#0a0a0a;text-decoration:underline;text-underline-offset:3px;cursor:pointer;padding:4px}
.zlt__res{margin-top:8px;background:#fff;border:1px solid rgba(10,10,10,.12);border-radius:6px;padding:10px 14px}
.zlt__res:focus{outline:none}
.zlt__rh{margin:0 0 4px;font:500 10.5px 'JetBrains Mono',monospace;letter-spacing:.06em;text-transform:uppercase;color:#5a5a5a}
.zlt__list{list-style:none;margin:0;padding:0}
.zlt__list li{display:grid;grid-template-columns:1fr auto;gap:1px 10px;padding:7px 0;border-top:1px solid rgba(10,10,10,.08)}
.zlt__list b{font-size:13.5px}
.zlt__list span{font-size:12px;color:#5a5a5a}
.zlt__km{grid-row:1/span 2;grid-column:2;align-self:center;font:600 12px 'JetBrains Mono',monospace;color:#0a0a0a!important}
.zlt__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;margin:0}
@media (prefers-reduced-motion:reduce){.zlt *{transition:none!important;animation:none!important}}// Zoeker met live teller · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
root=root.querySelector('.zlt')||root;
var sfx='-'+Math.random().toString(36).slice(2,7);root.querySelectorAll('[id]').forEach(function(el){var o=el.id,n=o+sfx;el.id=n;['for','aria-describedby'].forEach(function(a){root.querySelectorAll('['+a+'="'+o+'"]').forEach(function(x){x.setAttribute(a,n);});});});
var form=root.querySelector('.zlt__card'),pc=root.querySelector('.zlt__i'),pe=root.querySelector('.zlt__e'),chips=[].slice.call(root.querySelectorAll('.zlt__chip')),rg=root.querySelector('.zlt__range'),ro=root.querySelector('.zlt__ro'),go=root.querySelector('.zlt__go'),gt=root.querySelector('.zlt__gt'),wider=root.querySelector('.zlt__wider'),sr=root.querySelector('.zlt__sr'),res=root.querySelector('.zlt__res'),rh=root.querySelector('.zlt__rh'),list=root.querySelector('.zlt__list');
var CITY=[[10,'Amsterdam',52.37,4.90],[20,'Haarlem',52.38,4.64],[23,'Leiden',52.16,4.49],[25,'Den Haag',52.07,4.30],[26,'Delft',52.01,4.36],[30,'Rotterdam',51.92,4.48],[35,'Utrecht',52.09,5.12],[56,'Eindhoven',51.44,5.47],[80,'Zwolle',52.51,6.09],[97,'Groningen',53.22,6.57]];
var NAMES=['De Notenboom','Het Klavertje','De Vlinder','Zonnestraal','Kleine Reus','De Regenboog','Boomhut','Zeepbel','De Wilg','Duinroosje','Polderpret','Het Bolwerk','Kikkerland','De Speelark','Pinkeltje','Ukkepuk'];
var TYPES=['kdv','bso','peuter','gast'],TN={kdv:'Dagopvang',bso:'BSO',peuter:'Peuteropvang',gast:'Gastouder'},PLEK=['Direct plek','Plek vanaf jan','Plek vanaf mrt','Wachtlijst'];
var seed=11;function rnd(){seed=(seed*16807)%2147483647;return seed/2147483647;}
var LOC=[];CITY.forEach(function(c){for(var i=0;i<34;i++){var a=rnd()*6.283,d=Math.pow(rnd(),1.3)*16;LOC.push({n:NAMES[(i*5+c[0])%NAMES.length],t:TYPES[Math.floor(rnd()*4)],lat:c[2]+Math.sin(a)*d/111,lon:c[3]+Math.cos(a)*d/68,p:PLEK[Math.floor(rnd()*4)],c:c[1]});}});
function where(code){var n=parseInt(code.slice(0,4),10),pre=Math.floor(n/100),best=CITY[0];CITY.forEach(function(c){if(Math.abs(c[0]-pre)<Math.abs(best[0]-pre))best=c;});var off=n%100;return {lat:best[2]+((off%10)-5)*.004,lon:best[3]+(Math.floor(off/10)-5)*.006};}
function km(a,b){var dx=(a.lon-b.lon)*68,dy=(a.lat-b.lat)*111;return Math.sqrt(dx*dx+dy*dy);}
function code(){return pc.value.toUpperCase().replace(/[^0-9A-Z]/g,'');}
function pcValid(c){return /^[1-9]\d{3}[A-Z]{2}$/.test(c)&&!/^(SA|SD|SS)$/.test(c.slice(4));}
function types(){var s=chips.filter(function(c){return c.getAttribute('aria-pressed')==='true';}).map(function(c){return c.dataset.t;});return s.length?s:TYPES;}
function matches(){var c=code();if(!pcValid(c))return null;var me=where(c),r=+rg.value,ts=types();return LOC.map(function(l){return {l:l,d:km(me,l)};}).filter(function(x){return x.d<=r&&ts.indexOf(x.l.t)>=0;}).sort(function(a,b){return a.d-b.d;});}
var last=-1,st;
function upd(){var r=+rg.value;ro.textContent=r+' km';rg.style.setProperty('--p',((r-1)/24*100)+'%');rg.setAttribute('aria-valuetext',r+' kilometer');
var c=code(),m=matches();wider.hidden=true;
if(!c){go.setAttribute('aria-disabled','true');gt.textContent='Vul je postcode in';return;}
if(!m){go.setAttribute('aria-disabled','true');gt.textContent='Postcode onvolledig';return;}
var n=m.length;if(n){go.removeAttribute('aria-disabled');gt.innerHTML='Toon <b>'+n+'</b> '+(n===1?'locatie':'locaties');}
else{go.setAttribute('aria-disabled','true');gt.textContent='Geen locaties binnen '+r+' km';if(r<25){wider.hidden=false;wider.textContent='Zoek binnen '+Math.min(25,r+5)+' km';}}
if(n!==last){go.classList.remove('is-tick');void go.offsetWidth;go.classList.add('is-tick');clearTimeout(st);st=setTimeout(function(){sr.textContent=n?n+(n===1?' locatie':' locaties')+' gevonden':'Geen locaties gevonden';},600);last=n;}}
function fmtPc(){var v=pc.value,c=code().slice(0,6);pc.value=c.length>4?c.slice(0,4)+' '+c.slice(4):c;}
pc.addEventListener('input',function(){fmtPc();if(pc.getAttribute('aria-invalid')&&pcValid(code())){pc.removeAttribute('aria-invalid');pe.textContent='';}upd();});
pc.addEventListener('blur',function(){var c=code();if(c&&!pcValid(c)){pc.setAttribute('aria-invalid','true');pe.textContent='Een postcode is 4 cijfers en 2 letters, zoals 2514 AB';}});
chips.forEach(function(c){c.addEventListener('click',function(){c.setAttribute('aria-pressed',c.getAttribute('aria-pressed')==='true'?'false':'true');upd();});});
rg.addEventListener('input',upd);
wider.addEventListener('click',function(){rg.value=Math.min(25,+rg.value+5);upd();go.focus();});
form.addEventListener('submit',function(e){e.preventDefault();var c=code();
if(!pcValid(c)){pc.setAttribute('aria-invalid','true');pe.textContent=c?'Een postcode is 4 cijfers en 2 letters, zoals 2514 AB':'Vul je postcode in';pc.focus();return;}
var m=matches();if(!m.length)return;res.hidden=false;rh.textContent=m.length+(m.length===1?' locatie':' locaties')+' binnen '+rg.value+' km van '+pc.value+', dichtstbij eerst';
list.innerHTML=m.slice(0,4).map(function(x){return '<li><b>'+(x.l.t==='gast'?'Gastouder ':x.l.t==='bso'?'BSO ':'Kindcentrum ')+x.l.n+'</b><span>'+TN[x.l.t]+' · '+x.l.c+' · '+x.l.p+'</span><span class="zlt__km">'+x.d.toFixed(1).replace('.',',')+' km</span></li>';}).join('');res.focus({preventScroll:true});
root.scrollTop=root.scrollHeight;});
upd();
}
init(document.querySelector('.jouw-container'));
Meer uit formulieren & invoer
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 →