Tooltip + toggletip (anchor) Nieuw
Toegankelijke tooltip met pijl op hover én focus, in vier richtingen, plus een klik-infobolletje bij een prijs en een tooltip die uitlegt waarom een knop uit staat. Popover-API + CSS anchor positioning (position-area, flip bij te weinig ruimte); zonder anchor-support rekent een kleine JS-fallback de plek uit. Esc sluit, hover over de tooltip houdt hem open (WCAG 1.4.13).
Tooltip · vier kanten
boven
links
rechts
onder
Onderhoud cv-ketel
€ 89 per jaar
info@voorbeeld.nl
030 123 4567 · ma t/m vr 8:00 tot 17:30
Antwoord binnen een uur
Industrieweg 12, Utrecht
Beschikbaar zodra je akkoord geeft
Inclusief voorrijkosten en klein materiaal tot € 25. Zonder btw: € 73,55.
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.
<!-- Tooltip + toggletip (anchor) · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="tta">
<p class="tta__eyebrow">Tooltip · vier kanten</p>
<div class="tta__row" role="group" aria-label="Contact">
<div class="tta__cell"><button class="tta__ico" type="button" aria-label="Bellen" data-tip="tta-t2" aria-describedby="tta-t2"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 4h3l2 5-2.5 1.5a11 11 0 0 0 5 5L14 13l5 2v3a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2"/></svg></button><span class="tta__cap">boven</span></div>
<div class="tta__cell"><button class="tta__ico" type="button" aria-label="Mail" data-tip="tta-t1" aria-describedby="tta-t1"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg></button><span class="tta__cap">links</span></div>
<div class="tta__cell"><button class="tta__ico" type="button" aria-label="Route" data-tip="tta-t4" aria-describedby="tta-t4"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 21s-7-6.2-7-11a7 7 0 0 1 14 0c0 4.8-7 11-7 11z"/><circle cx="12" cy="10" r="2.5"/></svg></button><span class="tta__cap">rechts</span></div>
<div class="tta__cell"><button class="tta__ico" type="button" aria-label="Appen" data-tip="tta-t3" aria-describedby="tta-t3"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h16v11H9l-5 4z"/></svg></button><span class="tta__cap">onder</span></div>
</div>
<div class="tta__card">
<span class="tta__name">Onderhoud cv-ketel</span>
<span class="tta__price">€ 89 <small>per jaar</small></span>
<button class="tta__info" type="button" popovertarget="tta-tt" aria-label="Wat zit er in de prijs?">i</button>
<button class="tta__dl" type="button" aria-disabled="true" data-tip="tta-t5" aria-describedby="tta-t5">Offerte downloaden</button>
</div>
<div id="tta-t1" class="tta__tip" popover="manual" role="tooltip" data-place="left">info@voorbeeld.nl</div>
<div id="tta-t2" class="tta__tip" popover="manual" role="tooltip" data-place="top">030 123 4567 · ma t/m vr 8:00 tot 17:30</div>
<div id="tta-t3" class="tta__tip" popover="manual" role="tooltip" data-place="bottom">Antwoord binnen een uur</div>
<div id="tta-t4" class="tta__tip" popover="manual" role="tooltip" data-place="right">Industrieweg 12, Utrecht</div>
<div id="tta-t5" class="tta__tip" popover="manual" role="tooltip" data-place="top">Beschikbaar zodra je akkoord geeft</div>
<div id="tta-tt" class="tta__toggle" popover="auto" data-place="bottom"><span class="tta__live" role="status">Inclusief voorrijkosten en klein materiaal tot € 25. Zonder btw: € 73,55.</span></div>
</div>
</div>/* Tooltip + toggletip (anchor) · NedDev · eigen werk. */
.tta{--ink:#0a0a0a;--pd:#b83a0b;position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;padding:14px;font-family:'Switzer',system-ui,sans-serif;color:var(--ink)}
.tta__eyebrow{margin:0;font:600 10px/1 'JetBrains Mono',monospace;letter-spacing:.14em;text-transform:uppercase;color:#5b5b5b}
.tta__row{display:flex;gap:14px}
.tta__cell{display:flex;flex-direction:column;align-items:center;gap:6px}
.tta__cap{font:500 9.5px/1 'JetBrains Mono',monospace;letter-spacing:.08em;text-transform:uppercase;color:#5b5b5b}
.tta__ico{width:42px;height:42px;display:grid;place-items:center;padding:0;border:1px solid rgba(10,10,10,.18);border-radius:6px;background:#fff;color:var(--ink);cursor:pointer;transition:background .15s,border-color .15s,color .15s}
.tta__ico:hover{border-color:var(--ink)}
.tta__ico:active{background:var(--ink);color:#fff}
.tta__ico svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.tta__ico:focus-visible,.tta__info:focus-visible,.tta__dl:focus-visible{outline:2px solid var(--pd);outline-offset:2px}
.tta__card{display:flex;align-items:center;flex-wrap:wrap;justify-content:center;gap:8px 10px;background:#fff;border:1px solid rgba(10,10,10,.12);border-radius:6px;padding:10px 12px;max-width:100%}
.tta__name{font-size:13px;font-weight:600}
.tta__price{font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}
.tta__price small{font-weight:400;color:#5b5b5b;font-size:11px}
.tta__info{width:24px;height:24px;border-radius:50%;border:1px solid var(--ink);background:#fff;color:var(--ink);font:700 12px/1 'Switzer',system-ui,sans-serif;cursor:pointer;display:inline-grid;place-items:center;padding:0;transition:background .15s,color .15s}
.tta__info:hover,.tta__info[aria-expanded=true]{background:var(--ink);color:#fff}
.tta__dl{font:600 12px/1 'Switzer',system-ui,sans-serif;padding:9px 12px;border-radius:4px;border:1px dashed rgba(10,10,10,.3);background:#ebe9e5;color:#5b5b5b;cursor:not-allowed}
.tta__tip,.tta__toggle{position:fixed;inset:auto;margin:0;border:0;padding:7px 10px;background:var(--ink);color:#fff;font:500 12px/1.35 'Switzer',system-ui,sans-serif;border-radius:4px;max-width:220px;overflow:visible;opacity:0;transform:scale(.97);transition:opacity .14s ease,transform .14s ease,overlay .14s allow-discrete,display .14s allow-discrete}
.tta__toggle{background:#fff;color:var(--ink);border:1px solid var(--ink);max-width:230px;padding:10px 12px;font-size:12.5px}
.tta__tip:popover-open,.tta__toggle:popover-open{opacity:1;transform:none}
@starting-style{.tta__tip:popover-open,.tta__toggle:popover-open{opacity:0;transform:scale(.97)}}
.tta__tip::before,.tta__toggle::before{content:'';position:absolute;width:8px;height:8px;background:inherit;transform:rotate(45deg)}
.tta__tip[data-side=top]::before,.tta__toggle[data-side=top]::before{bottom:-4px;left:calc(var(--ax,50%) - 4px)}
.tta__tip[data-side=bottom]::before,.tta__toggle[data-side=bottom]::before{top:-4px;left:calc(var(--ax,50%) - 4px)}
.tta__tip[data-side=left]::before{right:-4px;top:calc(var(--ay,50%) - 4px)}
.tta__tip[data-side=right]::before{left:-4px;top:calc(var(--ay,50%) - 4px)}
.tta__toggle[data-side=bottom]::before{top:-5px;border-left:1px solid var(--ink);border-top:1px solid var(--ink)}
.tta__toggle[data-side=top]::before{bottom:-5px;border-right:1px solid var(--ink);border-bottom:1px solid var(--ink)}
@supports (anchor-name:--a){
.tta__tip[data-place=top]{position-area:top;margin-bottom:10px;position-try-fallbacks:flip-block}
.tta__tip[data-place=bottom]{position-area:bottom;margin-top:10px;position-try-fallbacks:flip-block}
.tta__tip[data-place=left]{position-area:left;margin-right:10px;position-try-fallbacks:flip-inline}
.tta__tip[data-place=right]{position-area:right;margin-left:10px;position-try-fallbacks:flip-inline}
.tta__toggle{position-area:bottom;margin-top:10px;position-try-fallbacks:flip-block}
}
@media (prefers-reduced-motion:reduce){.tta__tip,.tta__toggle{transition:none}}// Tooltip + toggletip (anchor) · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.tta');if(!w)return;
var U='-'+Math.random().toString(36).slice(2,7);
w.querySelectorAll('[id]').forEach(function(el){var o=el.id,n=o+U;el.id=n;['popovertarget','aria-describedby','data-tip'].forEach(function(a){w.querySelectorAll('['+a+'="'+o+'"]').forEach(function(x){x.setAttribute(a,n);});});});
var hasPop=typeof HTMLElement.prototype.showPopover==='function';
if(!hasPop){w.querySelectorAll('[data-tip]').forEach(function(t){var tip=document.getElementById(t.getAttribute('data-tip'));if(tip)t.title=tip.textContent;});return;}
var hasAnchor=!!(window.CSS&&CSS.supports('anchor-name: --a'));var k=0;
function bind(tr,tip){if(!hasAnchor)return;var nm='--tta'+U.slice(1)+(k++);tr.style.anchorName=nm;tip.style.positionAnchor=nm;}
function isOpen(t){return t.matches(':popover-open');}
function place(tr,tip){var r=tr.getBoundingClientRect(),bw=tip.offsetWidth,bh=tip.offsetHeight,vw=innerWidth,vh=innerHeight,g=10,p=tip.dataset.place,x,y;
if(!hasAnchor){
if(p==='top'&&r.top-bh-g<4)p='bottom';else if(p==='bottom'&&r.bottom+bh+g>vh-4)p='top';else if(p==='left'&&r.left-bw-g<4)p='right';else if(p==='right'&&r.right+bw+g>vw-4)p='left';
if(p==='top'||p==='bottom'){x=r.left+r.width/2-bw/2;y=p==='top'?r.top-bh-g:r.bottom+g;}else{y=r.top+r.height/2-bh/2;x=p==='left'?r.left-bw-g:r.right+g;}
x=Math.max(4,Math.min(vw-bw-4,x));y=Math.max(4,Math.min(vh-bh-4,y));tip.style.left=x+'px';tip.style.top=y+'px';
}else{var b=tip.getBoundingClientRect(),cx=b.left+b.width/2,cy=b.top+b.height/2,dx=cx-(r.left+r.width/2),dy=cy-(r.top+r.height/2);
if(Math.abs(dy)/(bh+r.height)>Math.abs(dx)/(bw+r.width))p=dy<0?'top':'bottom';else p=dx<0?'left':'right';x=cx-bw/2;y=cy-bh/2;}
tip.dataset.side=p;
if(p==='top'||p==='bottom')tip.style.setProperty('--ax',Math.max(10,Math.min(bw-10,r.left+r.width/2-x))+'px');else tip.style.setProperty('--ay',Math.max(8,Math.min(bh-8,r.top+r.height/2-y))+'px');}
var openT=null,timer=0;
function show(tr,tip){if(openT&&openT!==tip&&isOpen(openT))openT.hidePopover();if(!isOpen(tip))tip.showPopover();openT=tip;place(tr,tip);}
function hide(tip){if(isOpen(tip))tip.hidePopover();if(openT===tip)openT=null;}
w.querySelectorAll('[data-tip]').forEach(function(tr){var tip=document.getElementById(tr.getAttribute('data-tip'));if(!tip)return;bind(tr,tip);
tr.addEventListener('pointerenter',function(e){if(e.pointerType==='touch')return;clearTimeout(timer);timer=setTimeout(function(){show(tr,tip);},openT?0:280);});
tr.addEventListener('pointerleave',function(){clearTimeout(timer);timer=setTimeout(function(){hide(tip);},140);});
tip.addEventListener('pointerenter',function(){clearTimeout(timer);});
tip.addEventListener('pointerleave',function(){clearTimeout(timer);timer=setTimeout(function(){hide(tip);},140);});
tr.addEventListener('focus',function(){clearTimeout(timer);show(tr,tip);});
tr.addEventListener('blur',function(){clearTimeout(timer);hide(tip);});
if(tr.getAttribute('aria-disabled')==='true')tr.addEventListener('click',function(e){e.preventDefault();});});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&openT)hide(openT);});
if(!hasAnchor)window.addEventListener('scroll',function(){if(openT)hide(openT);var t=w.querySelector('.tta__toggle');if(t&&isOpen(t))t.hidePopover();},true);
var tb=w.querySelector('.tta__info'),tt=w.querySelector('.tta__toggle'),live=tt.querySelector('.tta__live'),txt=live.textContent;live.textContent='';bind(tb,tt);
tt.addEventListener('toggle',function(e){tb.setAttribute('aria-expanded',e.newState==='open');if(e.newState==='open'){live.textContent=txt;place(tb,tt);live.textContent='';setTimeout(function(){live.textContent=txt;place(tb,tt);},60);}else live.textContent='';});
}
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 →