Files
gott/wwwroot/js/tooltip.js
T

146 lines
4.0 KiB
JavaScript

// ── 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;
})();