// ── Custom Tooltip ── // Ersetzt native browser-tooltips für alle [title]-Elemente (function () { const DELAY = 200; let tooltipEl = null; let timeout = null; let currentTarget = null; function createTooltipEl() { const el = document.createElement('div'); el.className = 'tooltip'; document.body.appendChild(el); return el; } function show(target) { const text = target.getAttribute('data-tooltip'); if (!text) return; if (!tooltipEl) tooltipEl = createTooltipEl(); tooltipEl.textContent = ''; const lines = text.split('\n'); for (let i = 0; i < lines.length; i++) { if (i > 0) tooltipEl.appendChild(document.createElement('br')); tooltipEl.appendChild(document.createTextNode(lines[i])); } tooltipEl.classList.add('visible'); position(target); currentTarget = target; } function position(target) { if (!tooltipEl) return; const rect = target.getBoundingClientRect(); const gap = 8; // Reset für Messung tooltipEl.style.left = '0'; tooltipEl.style.top = '0'; const tw = tooltipEl.offsetWidth; const th = tooltipEl.offsetHeight; // Versuche oben zu platzieren let top = rect.top - th - gap; let left = rect.left + rect.width / 2 - tw / 2; // Wenn oben kein Platz → unten if (top < gap) { top = rect.bottom + gap; } // Horizontal begrenzen if (left < gap) left = gap; if (left + tw > window.innerWidth - gap) left = window.innerWidth - gap - tw; tooltipEl.style.left = left + 'px'; tooltipEl.style.top = top + 'px'; } function hide() { clearTimeout(timeout); timeout = null; if (tooltipEl) tooltipEl.classList.remove('visible'); if (currentTarget) { // title zurücksetzen const text = currentTarget.getAttribute('data-tooltip'); if (text) { currentTarget.setAttribute('title', text); currentTarget.removeAttribute('data-tooltip'); } currentTarget = null; } } function findTitleTarget(el) { while (el && el !== document.body) { if (el.getAttribute && (el.getAttribute('title') || el.getAttribute('data-tooltip'))) return el; el = el.parentElement; } return null; } document.addEventListener('mouseenter', function (e) { const target = findTitleTarget(e.target); if (!target) return; // Gleicher Target → nichts tun (Child-Wechsel innerhalb des gleichen Elements) if (target === currentTarget) { clearTimeout(timeout); return; } // title → data-tooltip verschieben (native tooltip unterdrücken) const title = target.getAttribute('title'); if (title) { target.setAttribute('data-tooltip', title); target.removeAttribute('title'); } const text = target.getAttribute('data-tooltip'); if (!text) return; // Falls bereits ein anderer tooltip offen → sofort wechseln if (currentTarget) { hide(); // title des neuen Targets nochmal verschieben (hide hat es nicht zurückgesetzt) if (target.getAttribute('title')) { target.setAttribute('data-tooltip', target.getAttribute('title')); target.removeAttribute('title'); } } clearTimeout(timeout); timeout = setTimeout(function () { show(target); }, DELAY); }, true); document.addEventListener('mouseleave', function (e) { const target = findTitleTarget(e.target); if (!target) return; if (target !== currentTarget && !timeout) return; // Prüfe ob die Maus zu einem Kind-Element des gleichen Tooltip-Targets geht var related = e.relatedTarget; if (related && target.contains(related)) return; hide(); }, true); // Auch bei Scroll/Resize verstecken document.addEventListener('scroll', hide, true); window.addEventListener('resize', hide); // Bei SPA-Navigation verstecken window.addEventListener('popstate', hide); window.addEventListener('hashchange', hide); // Global verfügbar für SPA-Router window.hideTooltip = hide; })();