Filterchips met popover Nieuw
Filterbalk voor woningaanbod: elke chip opent een popover met meerdere keuzes en aantallen per optie, actieve chips worden donker met een eigen wis-knop, met "Wis alles", een teller van actieve filters, een resultaatteller en een lege staat met één uitweg. Op smalle schermen één knop "Filters (3)" met alle groepen. Popover-API met CSS anchor positioning (fallback: JS-positie, en zonder popover-API een klasse-toggle met Esc en klik-buiten).
0 woningen
Geen filters actief
Geen woningen met deze filters. Haal een filter weg of begin opnieuw.
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.
<!-- Filterchips met popover · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="fcp"><div class="fcp__in">
<div class="fcp__bar">
<div class="fcp__chips" role="group" aria-label="Filters">
<span class="fcp__cw" data-g="plaats"><button type="button" class="fcp__chip" popovertarget="fcp-p-plaats"><span class="fcp__cl">Plaats</span><svg viewBox="0 0 10 6" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg></button><button type="button" class="fcp__x" aria-label="Wis filter Plaats" hidden><svg viewBox="0 0 10 10" aria-hidden="true"><path d="M2 2l6 6M8 2L2 8"/></svg></button></span>
<span class="fcp__cw" data-g="type"><button type="button" class="fcp__chip" popovertarget="fcp-p-type"><span class="fcp__cl">Type</span><svg viewBox="0 0 10 6" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg></button><button type="button" class="fcp__x" aria-label="Wis filter Type" hidden><svg viewBox="0 0 10 10" aria-hidden="true"><path d="M2 2l6 6M8 2L2 8"/></svg></button></span>
<span class="fcp__cw" data-g="prijs"><button type="button" class="fcp__chip" popovertarget="fcp-p-prijs"><span class="fcp__cl">Prijs</span><svg viewBox="0 0 10 6" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg></button><button type="button" class="fcp__x" aria-label="Wis filter Prijs" hidden><svg viewBox="0 0 10 10" aria-hidden="true"><path d="M2 2l6 6M8 2L2 8"/></svg></button></span>
<span class="fcp__cw" data-g="extra"><button type="button" class="fcp__chip" popovertarget="fcp-p-extra"><span class="fcp__cl">Extra</span><svg viewBox="0 0 10 6" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg></button><button type="button" class="fcp__x" aria-label="Wis filter Extra" hidden><svg viewBox="0 0 10 10" aria-hidden="true"><path d="M2 2l6 6M8 2L2 8"/></svg></button></span>
</div>
<button type="button" class="fcp__mob" popovertarget="fcp-p-all">Filters<span class="fcp__mn"></span></button>
</div>
<div class="fcp__sum"><p class="fcp__res" aria-live="polite"><b>0</b> woningen</p><p class="fcp__act"><span class="fcp__ac">Geen filters actief</span><button type="button" class="fcp__clear" hidden>Wis alles</button></p></div>
<ul class="fcp__list"></ul>
<div class="fcp__empty" hidden><p><b>Geen woningen met deze filters.</b> Haal een filter weg of begin opnieuw.</p><button type="button" class="fcp__clear2">Wis alle filters</button></div>
<div class="fcp__pop" id="fcp-p-plaats" popover data-g="plaats" role="dialog" aria-label="Filter op plaats"></div>
<div class="fcp__pop" id="fcp-p-type" popover data-g="type" role="dialog" aria-label="Filter op type woning"></div>
<div class="fcp__pop" id="fcp-p-prijs" popover data-g="prijs" role="dialog" aria-label="Filter op prijs"></div>
<div class="fcp__pop" id="fcp-p-extra" popover data-g="extra" role="dialog" aria-label="Filter op extra's"></div>
<div class="fcp__pop fcp__pop--all" id="fcp-p-all" popover data-g="all" role="dialog" aria-label="Alle filters"></div>
</div></div>
</div>/* Filterchips met popover · NedDev · eigen werk. */
.fcp [hidden]{display:none!important}
.fcp{position:absolute;inset:0;overflow:auto;padding:12px;box-sizing:border-box;background:#f4f3f1;font-family:'Switzer',system-ui,sans-serif;color:#0a0a0a;container-type:inline-size;scrollbar-width:thin}
.fcp *,.fcp *::before,.fcp *::after{box-sizing:border-box}
.fcp__in{position:relative;width:100%;max-width:880px;margin:0 auto}
.fcp__bar{display:flex;align-items:center;gap:6px}
.fcp__chips{display:flex;flex-wrap:wrap;gap:6px}
.fcp__cw{display:inline-flex;border-radius:100px}
.fcp__chip{display:inline-flex;align-items:center;gap:6px;font:500 13px 'Switzer',system-ui,sans-serif;padding:6px 12px;border:1px solid #8c8c8c;border-radius:100px;background:#fff;color:#0a0a0a;cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}
.fcp__chip svg{width:9px;height:6px;fill:none;stroke:currentColor;stroke-width:1.6;transition:transform .15s}
.fcp__chip:hover{border-color:#0a0a0a}
.fcp__chip[aria-expanded="true"] svg,.fcp__chip.is-open svg{transform:rotate(180deg)}
.fcp__cw.is-on .fcp__chip{background:#0a0a0a;border-color:#0a0a0a;color:#fff;border-radius:100px 0 0 100px;padding-right:8px}
.fcp__x{display:grid;place-items:center;width:28px;border:1px solid #0a0a0a;border-left-color:rgba(255,255,255,.25);background:#0a0a0a;color:#fff;border-radius:0 100px 100px 0;cursor:pointer;transition:background .15s,color .15s}
.fcp__x svg{width:9px;height:9px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round}
.fcp__x:hover{background:#F5480F;color:#0a0a0a}
.fcp__chip:focus-visible,.fcp__x:focus-visible,.fcp__mob:focus-visible,.fcp__clear:focus-visible,.fcp__clear2:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.fcp__mob{display:none;align-items:center;gap:6px;font:600 13px 'Switzer',system-ui,sans-serif;padding:7px 14px;border:1px solid #0a0a0a;border-radius:100px;background:#fff;color:#0a0a0a;cursor:pointer}
.fcp__mob:hover{background:#0a0a0a;color:#fff}
.fcp__mn:not(:empty){display:inline-grid;place-items:center;min-width:18px;height:18px;padding:0 4px;border-radius:100px;background:#F5480F;color:#0a0a0a;font:700 11px 'JetBrains Mono',monospace}
@container (max-width:379px){.fcp__chips{display:none}.fcp__mob{display:inline-flex}}
.fcp__sum{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:4px 10px;margin:10px 0 8px}
.fcp__res{margin:0;font-size:14px}
.fcp__res b{font-variant-numeric:tabular-nums}
.fcp__act{margin:0;display:flex;align-items:center;gap:8px;font:500 11px 'JetBrains Mono',monospace;color:#5a5a5a}
.fcp__clear,.fcp__clear2{font:600 12.5px 'Switzer',system-ui,sans-serif;background:none;border:0;padding:3px;color:#0a0a0a;text-decoration:underline;text-underline-offset:3px;cursor:pointer}
.fcp__clear:hover,.fcp__clear2:hover{color:#B8360A}
.fcp__list{list-style:none;margin:0;padding:0;display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(160px,1fr))}
.fcp__list li{background:#fff;border:1px solid rgba(10,10,10,.1);border-radius:6px;padding:9px 11px;display:flex;flex-direction:column;gap:2px}
.fcp__list b{font-size:14px}
.fcp__list span{font-size:12px;color:#5a5a5a}
.fcp__list small{font:500 10px 'JetBrains Mono',monospace;letter-spacing:.04em;color:#0a0a0a;margin-top:3px}
.fcp__empty{background:#fff;border:1px dashed rgba(10,10,10,.3);border-radius:6px;padding:16px;text-align:center;font-size:13px;line-height:1.5}
.fcp__empty p{margin:0 0 6px}
.fcp__pop{inset:auto;margin:0;width:240px;max-width:calc(100vw - 24px);max-height:min(340px,70vh);overflow:auto;padding:12px;background:#fff;color:#0a0a0a;border:1px solid #0a0a0a;border-radius:6px;box-shadow:0 14px 40px rgba(10,10,10,.18);font-family:'Switzer',system-ui,sans-serif}
.fcp__pop--all{width:300px}
.fcp__pop.is-fb{display:none;position:absolute;z-index:20}
.fcp__pop.is-fb.is-open{display:block}
@supports (anchor-name:--a){.fcp__pop.is-anc{position-area:bottom span-right;position-try-fallbacks:flip-inline,flip-block;margin-top:6px}}
.fcp__fs{border:0;margin:0 0 10px;padding:0;min-width:0}
.fcp__lg{padding:0;font:600 10.5px 'JetBrains Mono',monospace;letter-spacing:.08em;text-transform:uppercase;color:#5a5a5a;margin-bottom:6px}
.fcp__opt{display:flex;align-items:center;gap:8px;padding:5px 4px;border-radius:4px;font-size:13.5px;cursor:pointer}
.fcp__opt:hover{background:#f4f3f1}
.fcp__opt input{width:16px;height:16px;margin:0;accent-color:#0a0a0a;flex:none}
.fcp__opt input:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.fcp__opt em{margin-left:auto;font:500 11px 'JetBrains Mono',monospace;font-style:normal;color:#5a5a5a}
.fcp__opt.is-zero{color:#767676}
.fcp__pf{display:flex;justify-content:space-between;align-items:center;gap:8px;border-top:1px solid rgba(10,10,10,.1);padding-top:10px;position:sticky;bottom:-12px;background:#fff;margin:0 -12px -12px;padding:10px 12px 12px}
.fcp__pw{font:600 12.5px 'Switzer',system-ui,sans-serif;background:none;border:0;color:#0a0a0a;text-decoration:underline;cursor:pointer;padding:4px}
.fcp__pd{font:600 13px 'Switzer',system-ui,sans-serif;background:#0a0a0a;color:#fff;border:1px solid #0a0a0a;border-radius:4px;padding:7px 12px;cursor:pointer;transition:background .15s,color .15s}
.fcp__pd:hover{background:#F5480F;border-color:#F5480F;color:#0a0a0a}
.fcp__pw:focus-visible,.fcp__pd:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
@media (prefers-reduced-motion:reduce){.fcp *{transition:none!important}}// Filterchips met popover · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
root=root.querySelector('.fcp')||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;root.querySelectorAll('[popovertarget="'+o+'"]').forEach(function(x){x.setAttribute('popovertarget',n);});});
var G={plaats:{l:'Plaats',multi:1,o:['Utrecht','Zeist','Houten','Nieuwegein','Bunnik']},type:{l:'Type woning',multi:1,o:['Appartement','Tussenwoning','Hoekwoning','2-onder-1-kap','Vrijstaand']},prijs:{l:'Prijs',multi:0,o:['Tot € 350.000','€ 350.000 tot € 500.000','€ 500.000 tot € 750.000','Vanaf € 750.000']},extra:{l:"Extra's",multi:1,o:['Tuin','Balkon','Parkeerplaats','Energielabel A']}};
var KEYS=['plaats','type','prijs','extra'],BANDS=[[0,350000],[350000,500000],[500000,750000],[750000,1e9]];
var STR=['Lindenlaan','Kastanjelaan','Molenweg','Kerkstraat','Beukenlaan','Vondellaan','Julianalaan','Wilhelminapark','Hoofdstraat','Slotlaan','Fazantenlaan','Waterlinieweg'];
var seed=5;function rnd(){seed=(seed*16807)%2147483647;return seed/2147483647;}
var H=[];for(var i=0;i<52;i++){var t=Math.floor(rnd()*5),base=[290000,385000,440000,560000,820000][t],pr=Math.round((base*(.75+rnd()*.6))/5000)*5000,ex=[];
if(t>0&&rnd()<.8)ex.push('Tuin');if(t===0&&rnd()<.75)ex.push('Balkon');if(rnd()<.45)ex.push('Parkeerplaats');if(rnd()<.35)ex.push('Energielabel A');
H.push({plaats:G.plaats.o[Math.floor(rnd()*5)],type:G.type.o[t],prijs:pr,extra:ex,m2:[68,98,112,134,176][t]+Math.round(rnd()*30),k:[2,4,4,5,6][t]+(rnd()<.3?1:0),str:STR[i%STR.length]+' '+(3+Math.floor(rnd()*140))});}
var S={plaats:[],type:[],prijs:[],extra:[]};
function band(p){for(var j=0;j<BANDS.length;j++)if(p>=BANDS[j][0]&&p<BANDS[j][1])return G.prijs.o[j];}
function ok(h,skip){return KEYS.every(function(k){if(k===skip||!S[k].length)return true;if(k==='plaats')return S.plaats.indexOf(h.plaats)>=0;if(k==='type')return S.type.indexOf(h.type)>=0;if(k==='prijs')return S.prijs.indexOf(band(h.prijs))>=0;return S.extra.every(function(x){return h.extra.indexOf(x)>=0;});});}
function cnt(k,v){return H.filter(function(h){if(!ok(h,k))return false;if(k==='plaats')return h.plaats===v;if(k==='type')return h.type===v;if(k==='prijs')return band(h.prijs)===v;return S.extra.concat([v]).every(function(x){return h.extra.indexOf(x)>=0;});}).length;}
function eur(n){return '€ '+n.toLocaleString('nl-NL');}
function esc(s){return String(s).replace(/[&<>"]/g,function(c){return {'&':'&','<':'<','>':'>','"':'"'}[c];});}
function fsHtml(k){var g=G[k],nm='fcp-'+k+sfx;return '<fieldset class="fcp__fs"><legend class="fcp__lg">'+esc(g.l)+'</legend>'+g.o.map(function(v){return '<label class="fcp__opt"><input type="'+(g.multi?'checkbox':'radio')+'" name="'+nm+'" data-g="'+k+'" value="'+esc(v)+'"><span>'+esc(v)+'</span><em data-c></em></label>';}).join('')+'</fieldset>';}
var pops=[].slice.call(root.querySelectorAll('.fcp__pop'));
pops.forEach(function(p){var k=p.dataset.g,all=k==='all';p.innerHTML=(all?KEYS.map(fsHtml).join(''):fsHtml(k))+'<div class="fcp__pf"><button type="button" class="fcp__pw">'+(all?'Wis alles':'Wissen')+'</button><button type="button" class="fcp__pd">Toon woningen</button></div>';});
var list=root.querySelector('.fcp__list'),resB=root.querySelector('.fcp__res'),ac=root.querySelector('.fcp__ac'),clr=root.querySelector('.fcp__clear'),empty=root.querySelector('.fcp__empty'),mn=root.querySelector('.fcp__mn'),mob=root.querySelector('.fcp__mob');
function render(){var r=H.filter(function(h){return ok(h);}),n=0;KEYS.forEach(function(k){n+=S[k].length;});
resB.innerHTML='<b>'+r.length+'</b> '+(r.length===1?'woning':'woningen');ac.textContent=n?n+(n===1?' filter actief':' filters actief'):'Geen filters actief';clr.hidden=!n;mn.textContent=n?n:'';mob.setAttribute('aria-label','Filters'+(n?', '+n+' actief':''));
list.innerHTML=r.slice(0,6).map(function(h){return '<li><b>'+eur(h.prijs)+' k.k.</b><span>'+esc(h.str)+', '+esc(h.plaats)+'</span><span>'+esc(h.type)+' · '+h.k+' kamers · '+h.m2+' m²</span>'+(h.extra.length?'<small>'+esc(h.extra.join(' · '))+'</small>':'')+'</li>';}).join('');
list.hidden=!r.length;empty.hidden=!!r.length;
KEYS.forEach(function(k){var w=root.querySelector('.fcp__cw[data-g="'+k+'"]'),s=S[k],l=w.querySelector('.fcp__cl');w.classList.toggle('is-on',!!s.length);w.querySelector('.fcp__x').hidden=!s.length;
l.textContent=!s.length?G[k].l.replace(' woning',''):s.length===1?(k==='prijs'?s[0].replace(' tot ','-').replace(/\.000/g,'k').replace(/€ /g,'€'):s[0]):G[k].l.replace(' woning','')+' · '+s.length;
w.querySelector('.fcp__chip').setAttribute('aria-label',G[k].l+(s.length?', '+s.join(', '):', geen filter'));});
root.querySelectorAll('.fcp__opt input').forEach(function(inp){var k=inp.dataset.g,v=inp.value,c=cnt(k,v),on=S[k].indexOf(v)>=0;inp.checked=on;var lab=inp.closest('.fcp__opt');lab.querySelector('[data-c]').textContent=c;lab.classList.toggle('is-zero',!c&&!on);});
root.querySelectorAll('.fcp__pd').forEach(function(b){b.textContent='Toon '+r.length+(r.length===1?' woning':' woningen');});}
root.addEventListener('change',function(e){var inp=e.target;if(!inp.dataset||!inp.dataset.g)return;var k=inp.dataset.g,v=inp.value;
if(G[k].multi){var i=S[k].indexOf(v);if(inp.checked&&i<0)S[k].push(v);if(!inp.checked&&i>=0)S[k].splice(i,1);}else S[k]=inp.checked?[v]:[];render();});
function clearAll(){KEYS.forEach(function(k){S[k]=[];});render();}
root.querySelectorAll('.fcp__x').forEach(function(x){x.addEventListener('click',function(){var w=x.closest('.fcp__cw');S[w.dataset.g]=[];render();w.querySelector('.fcp__chip').focus();});});
function back(){var c=root.querySelector('.fcp__chip');(c&&c.offsetParent?c:mob).focus();}
clr.addEventListener('click',function(){clearAll();back();});
root.querySelector('.fcp__clear2').addEventListener('click',function(){clearAll();back();});
var NATIVE=typeof HTMLElement!=='undefined'&&HTMLElement.prototype.hasOwnProperty('popover'),ANC=!!(window.CSS&&CSS.supports&&CSS.supports('anchor-name','--a'));
function inv(p){return root.querySelector('[popovertarget="'+p.id+'"]');}
function hide(p){if(NATIVE){try{p.hidePopover();}catch(_){}}else{p.classList.remove('is-open');var b=inv(p);if(b){b.classList.remove('is-open');b.setAttribute('aria-expanded','false');b.focus();}}}
pops.forEach(function(p){p.addEventListener('click',function(e){var b=e.target.closest('button');if(!b)return;
if(b.classList.contains('fcp__pw')){if(p.dataset.g==='all')clearAll();else{S[p.dataset.g]=[];render();}}
if(b.classList.contains('fcp__pd'))hide(p);});});
if(NATIVE){var aid='--fcp'+sfx.slice(1);
pops.forEach(function(p){p.addEventListener('toggle',function(e){if(e.newState!=='open')return;var b=inv(p);if(!b)return;
if(ANC){root.querySelectorAll('[popovertarget]').forEach(function(x){x.style.anchorName='';});b.style.anchorName=aid;p.style.positionAnchor=aid;p.classList.add('is-anc');}
else{var r=b.getBoundingClientRect(),w=p.offsetWidth,h=p.offsetHeight,left=Math.max(8,Math.min(r.left,innerWidth-w-8)),top=r.bottom+6;if(top+h>innerHeight-8&&r.top-h-6>8)top=r.top-h-6;p.style.position='fixed';p.style.left=left+'px';p.style.top=top+'px';}
var f=p.querySelector('input');if(f)f.focus();});});
if(!ANC)root.addEventListener('scroll',function(){pops.forEach(function(p){try{if(p.matches(':popover-open'))p.hidePopover();}catch(_){}});},{passive:true});
}else{
pops.forEach(function(p){p.classList.add('is-fb');p.removeAttribute('popover');});
root.querySelectorAll('[popovertarget]').forEach(function(b){b.setAttribute('aria-expanded','false');b.addEventListener('click',function(){var p=root.querySelector('#'+b.getAttribute('popovertarget')),open=p.classList.contains('is-open');
pops.forEach(function(q){q.classList.remove('is-open');});root.querySelectorAll('[popovertarget]').forEach(function(x){x.classList.remove('is-open');x.setAttribute('aria-expanded','false');});
if(!open){var ir=root.querySelector('.fcp__in').getBoundingClientRect(),r=b.getBoundingClientRect();p.style.left=Math.max(0,Math.min(r.left-ir.left,ir.width-p.offsetWidth-0))+'px';p.style.top=(r.bottom-ir.top+6)+'px';p.classList.add('is-open');b.classList.add('is-open');b.setAttribute('aria-expanded','true');var f=p.querySelector('input');if(f)f.focus();}});});
root.addEventListener('keydown',function(e){if(e.key==='Escape'){var p=root.querySelector('.fcp__pop.is-open');if(p)hide(p);}});
document.addEventListener('click',function(e){if(!root.contains(e.target))return;if(e.target.closest('.fcp__pop,[popovertarget]'))return;root.querySelectorAll('.fcp__pop.is-open').forEach(hide);});
}
S.plaats=['Utrecht','Zeist'];S.extra=['Tuin'];render();
}
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 →