// 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 = []; }