178 lines
6.0 KiB
JavaScript
178 lines
6.0 KiB
JavaScript
// Alias für die Queryselector Methoden
|
|
let $$ = document.querySelector.bind(document);
|
|
let $$$ = document.querySelectorAll.bind(document);
|
|
// Array für momentan angezeigte/aktive Tooltips
|
|
let visibleTooltips = [];
|
|
|
|
/**
|
|
* Dieses Script funktioniert wie folgt:
|
|
* Einem beliebigen HTML Element kann ein Attribut mit dem Namen `data-tooltip`
|
|
* hinzugefügt werden. Dieses kann folgendes enthalten:
|
|
* 1. Den Tooltip Text selbst: data-tooltip='Mein Tooltip Text hier'
|
|
* 2. Eine ID eines anderen Elements: data-tooltip='#mein-tooltip-elem'
|
|
* Der `#` ist hierbei wichtig!
|
|
* Methode 2 kann z.B. für etwas komplexere Tooltips verwendet werden
|
|
*
|
|
* Die Styles von Tooltips findet man in _layout.scss (.ttip, .ttip-triangle)
|
|
*/
|
|
document.addEventListener("mouseover", e => {
|
|
let t = e.target;
|
|
if (typeof t.dataset.tooltip !== "undefined") {
|
|
// Mehrfaches erstellen bei hinundher zwischen Tooltip und Caller vermeiden
|
|
removeTooltips();
|
|
let ttip = positionTooltip(t);
|
|
// Nicht sicher ob das tatsächlich die eleganteste Variante ist.
|
|
// Man könnte auch ein unsichtbares Element verwenden, das die Lücke
|
|
// zwischen Caller und Tooltip ausfüllt um ein "Übergehen" auf das
|
|
// Tooltip zu ermöglichen.
|
|
// Hier hat man 500ms Zeit um das zu tun.
|
|
let oneshot = e => {
|
|
setTimeout((e) => {
|
|
let onTooltip = false;
|
|
$$$(":hover").forEach(el => {
|
|
if (el.className.includes("ttip")) {
|
|
onTooltip = true;
|
|
return;
|
|
}
|
|
if (typeof el.dataset.tooltip !== "undefined") {
|
|
onTooltip = true;
|
|
return;
|
|
}
|
|
});
|
|
if (!onTooltip) {
|
|
removeTooltips();
|
|
t.removeEventListener("mouseleave", oneshot);
|
|
}
|
|
}, 500);
|
|
};
|
|
|
|
t.addEventListener("mouseleave", oneshot);
|
|
|
|
ttip.addEventListener("mouseleave", e => {
|
|
removeTooltips();
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Positioniert ein Tooltip abhänging von der Position von `caller` im Viewport
|
|
* und erzeugt ein kleines Dreieck, dass auf den `caller` zeigt.
|
|
* @param {object} caller Ein beliebiges Element mit einem `data` feld data-tooltip
|
|
* @return {object}
|
|
*/
|
|
function positionTooltip(caller) {
|
|
// Computed Style des Callers in Bezug auf den Viewport
|
|
let cBR = caller.getBoundingClientRect();
|
|
// Tooltip anfragen
|
|
let tooltip = getTooltip(caller);
|
|
// Farbe des kleinen Tooltip Dreiecks
|
|
let triColor = "white";
|
|
let triSize = 5;
|
|
// Dreieck für Tooltip erstellen
|
|
let tri = document.createElement("div");
|
|
tri.classList = "ttip-triangle";
|
|
tri.style.borderLeft = triSize + "px solid transparent";
|
|
tri.style.borderRight = triSize + "px solid transparent";
|
|
|
|
// Bestimmen ob simples Tooltip mit Text aus data-tooltip
|
|
// oder komplexeres Tooltip aus Element mit ID XYZ angefordert wird
|
|
if (typeof tooltip === "string") {
|
|
let text = tooltip;
|
|
tooltip = document.createElement("div");
|
|
// Element zum Löschen mit Klasse del-ttip ausstatten (func removeTooltips)
|
|
tooltip.classList = "ttip del-ttip";
|
|
// Tooltip mit Text befüllen
|
|
tooltip.innerHTML = text;
|
|
document.body.appendChild(tooltip);
|
|
} else {
|
|
tooltip.style.display = "block";
|
|
tooltip.classList = "ttip";
|
|
}
|
|
// Dreieck in Tooltipfenster einfügen
|
|
tooltip.appendChild(tri);
|
|
|
|
// Position des Anfordenden Elements
|
|
let hPos = cBR.left + cBR.width / 2;
|
|
let vPos = cBR.top + cBR.height / 2;
|
|
// Mittelpunkt
|
|
let hMid = window.innerWidth / 2;
|
|
let vMid = window.innerHeight / 2;
|
|
// Position des Tooltips
|
|
let ttop;
|
|
let tleft;
|
|
// Position des Dreiecks
|
|
let mtop;
|
|
|
|
tooltip.style.position = "fixed";
|
|
|
|
let tBR = tooltip.getBoundingClientRect();
|
|
let triBR = tri.getBoundingClientRect();
|
|
// Die Position des "Tooltip-Callers" in diesem Schema:
|
|
/*
|
|
-----------------
|
|
| | |
|
|
| o | |
|
|
| | o |
|
|
-----------------
|
|
| | o |
|
|
| | |
|
|
|o | |
|
|
-----------------
|
|
*/
|
|
// bestimmt die Positionierung des Tooltips und des kleinen Dreiecks
|
|
// Links
|
|
if (hPos <= hMid) {
|
|
tleft = cBR.left;
|
|
// Dreieck positionieren
|
|
tri.style.left = cBR.width / 2 - triSize + "px";
|
|
// Rechts
|
|
} else {
|
|
tleft = cBR.right - tBR.width;
|
|
// Dreieck positionieren
|
|
tri.style.right = cBR.width / 2 - triSize + "px";
|
|
}
|
|
// Oben
|
|
if (vPos <= vMid) {
|
|
// Tooltip Abstand zum Caller
|
|
ttop = cBR.bottom + triSize;
|
|
// Dreieck positionieren
|
|
tri.style.borderBottom = triSize + "px solid " + triColor;
|
|
tri.style.top = -triSize + "px";
|
|
// Unten
|
|
} else {
|
|
// Tooltip Abstand zum Caller
|
|
ttop = cBR.top - tBR.height - triSize;
|
|
// Dreieck positioniere
|
|
tri.style.borderTop = triSize + "px solid " + triColor;
|
|
tri.style.bottom = -triSize + "px";
|
|
}
|
|
|
|
// Tooltip
|
|
tooltip.style.top = ttop + "px";
|
|
tooltip.style.left = tleft + "px";
|
|
|
|
// Zum Array der momentan angezeigten Tooltips hinzufügen um später zu entfernen
|
|
visibleTooltips.push(tooltip);
|
|
return tooltip;
|
|
}
|
|
|
|
function getTooltip(element) {
|
|
let tt = element.dataset.tooltip;
|
|
// Entweder das Tooltip Element oder den Tooltip Text zurückgeben
|
|
return (/^#.+$/.test(tt)) ? $$(tt) : tt;
|
|
}
|
|
|
|
function removeTooltips() {
|
|
// Tooltips ausblenden/entfernen, je nach dem ob dynamisch erzeugt oder nicht
|
|
visibleTooltips.forEach(ele => {
|
|
if (ele.className.includes("del-ttip")) {
|
|
document.body.removeChild(ele);
|
|
} else {
|
|
ele.style.display = "none";
|
|
// Hinzugefügtes Dreieck entfernen
|
|
ele.removeChild(ele.lastChild);
|
|
}
|
|
});
|
|
// Array leeren, da alle Tooltips weg sein sollten
|
|
visibleTooltips = [];
|
|
} |