NEWS
Mit Maus über ein Widget und Info Text anzeigen lassen
-
Wie ich im Forum gelesen habe funktioniert das Maus over in VIS1 nicht, weiß jemand ob das mittlerweile in VIS2 geht?
Ich möchte mir einen Info Text anzeigen lassen...Grüße
-
in welchem post im forum wurde das geschrieben?
welches widget ist gemeint. es gibt viele.gibt es da tatsächlich was standardmäßiges?
ansonsten könnte man das mit ein wenig javascript und jquery was in vis1/2 verfügbar ist selbst bauen. -
@oliverio
Das kann ich leider selber nicht, da kenne ich nicht nicht aus...
Den Forum Eintrag müsste ich nochmal raussuchen -
Tooltips für widgets in vis1 und vis2 <-- Überschrift für die Suchmaschine
so eine kleine Sonntag Nachmittag Beschäftigung
funktioniert in vis1 und vis2Der Code besteht aus 2 teilen.
Teil 1 ist der Basiscode
Teil 2 ist die Konfiguration und besteht aus einem Array.
Für jedes widget kommt da ein Eintrag rein. Was genau notwendig ist steht am Anfang des Basiscodes in der Erklärung.
Der Basiscode enthält auch noch ein paar Managementfunktionen, bei dem man den Tooltip sogar dynamisch ändern könnt.
Der Init teil muss nach dem Basiscode im script tab stehen.(function () { /** * Tooltip-Konfiguration * --------------------- * Jeder Tooltip wird über ein Objekt definiert, z. B.: * * { * key: 'unique-id', // Eindeutiger Schlüssel für diesen Tooltip (Pflicht) * widgetId: 'w00000', // ID des Widgets im DOM (optional, wenn selector gesetzt) * selector: '#w00000 .help-icon', // Optionaler CSS-Selektor für ein bestimmtes Inneres Element * content: 'Tooltip-Inhalt <b>HTML</b> erlaubt', // Text/HTML, der im Tooltip angezeigt wird * placement: 'top' | 'bottom' | 'left' | 'right' | 'auto', // Position (Standard: 'top') * offset: 8 // Abstand in Pixeln zum Ziel (Standard: 8) * } * * * Öffentliche API-Funktionen (window.Tooltips) * -------------------------------------------- * * Tooltips.init(configArray) * - Initialisiert das Tooltip-System mit einer Liste von Konfigurationsobjekten. * * Tooltips.add(config) * - Fügt einen neuen Tooltip hinzu (siehe oben für Felder). * * Tooltips.update(key, patchObject) * - Aktualisiert bestehende Tooltip-Konfiguration (z. B. Content ändern). * * Tooltips.remove(key) * - Entfernt einen Tooltip anhand seines Schlüssels. * * Tooltips.clear() * - Entfernt alle registrierten Tooltips und blendet den aktuellen aus. * * Tooltips.destroy() * - Deaktiviert das gesamte Tooltip-System, entfernt Styles und Event-Handler. * * * Hinweise * -------- * - `widgetId` genügt, wenn der Tooltip immer am äußeren Widget erscheinen soll. * - `selector` überschreibt `widgetId`, damit kannst du innere Elemente exakt anvisieren. * - `placement: 'auto'` versucht die beste Richtung zu wählen, wenn nicht genug Platz ist. * - Delegierte Events sorgen dafür, dass auch dynamisch hinzugefügte Widgets automatisch funktionieren. */ // ===== Minimalistisches CSS einmal injizieren ===== const STYLE_ID = 'vanilla-tooltips-style'; if (!document.getElementById(STYLE_ID)) { const css = ` .vtp-tooltip { position: fixed; z-index: 999999; max-width: 280px; padding: 8px 10px; border-radius: 6px; box-shadow: 0 6px 24px rgba(0,0,0,.18), 0 2px 8px rgba(0,0,0,.12); background: rgba(30, 30, 35, .96); color: #fff; font: 12px/1.35 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; pointer-events: none; opacity: 0; transform: translateY(-2px); transition: opacity .12s ease, transform .12s ease; } .vtp-tooltip[data-show="true"] { opacity: 1; transform: translateY(0); } .vtp-arrow { position: absolute; width: 8px; height: 8px; background: inherit; transform: rotate(45deg); } .vtp-tooltip[data-placement="top"] .vtp-arrow { bottom: -4px; left: 50%; transform: translateX(-50%) rotate(45deg); } .vtp-tooltip[data-placement="bottom"] .vtp-arrow { top: -4px; left: 50%; transform: translateX(-50%) rotate(45deg); } .vtp-tooltip[data-placement="left"] .vtp-arrow { right: -4px; top: 50%; transform: translateY(-50%) rotate(45deg); } .vtp-tooltip[data-placement="right"] .vtp-arrow { left: -4px; top: 50%; transform: translateY(-50%) rotate(45deg); } /* Fokus-Hint für Tastatur-Nutzer */ [data-vtp-target]:focus { outline: 2px solid rgba(100, 150, 255, .7); outline-offset: 2px; } `; const style = document.createElement('style'); style.id = STYLE_ID; style.textContent = css; document.head.appendChild(style); } // ===== Util ===== const $doc = window.jQuery ? jQuery(document) : null; if (!$doc) { console.error('[Tooltips] jQuery ist erforderlich (ohne jQuery-UI).'); return; } const clamp = (v, min, max) => Math.min(Math.max(v, min), max); // ===== Tooltip DOM (ein einziges, wiederverwendetes Element) ===== const tooltipEl = document.createElement('div'); tooltipEl.className = 'vtp-tooltip'; tooltipEl.setAttribute('role', 'tooltip'); tooltipEl.setAttribute('aria-hidden', 'true'); tooltipEl.innerHTML = `<div class="vtp-content"></div><div class="vtp-arrow" aria-hidden="true"></div>`; document.body.appendChild(tooltipEl); const contentEl = tooltipEl.querySelector('.vtp-content'); // ===== State ===== const registry = new Map(); // key -> config let current = { key: null, target: null, placement: 'top' }; let observer = null; // ===== Positionierung ===== function placeTooltip(target, placement, offset = 8) { const rect = target.getBoundingClientRect(); const tipRect = tooltipEl.getBoundingClientRect(); const vw = window.innerWidth; const vh = window.innerHeight; const candidates = (placement === 'auto') ? ['top', 'bottom', 'right', 'left'] : [placement]; let chosen = candidates[0], x = 0, y = 0; const fits = { top: rect.top >= tipRect.height + offset, bottom: vh - rect.bottom >= tipRect.height + offset, left: rect.left >= tipRect.width + offset, right: vw - rect.right >= tipRect.width + offset }; if (placement === 'auto') { // Priorität: top > bottom > right > left (anpassbar) chosen = (fits.top && 'top') || (fits.bottom && 'bottom') || (fits.right && 'right') || (fits.left && 'left') || 'top'; } else if (!fits[placement]) { // Fallback auf eine passende Seite chosen = (fits.top && 'top') || (fits.bottom && 'bottom') || (fits.right && 'right') || (fits.left && 'left') || placement; } // Vorläufige Position if (chosen === 'top') { x = rect.left + rect.width / 2 - tipRect.width / 2; y = rect.top - tipRect.height - offset; } if (chosen === 'bottom') { x = rect.left + rect.width / 2 - tipRect.width / 2; y = rect.bottom + offset; } if (chosen === 'left') { x = rect.left - tipRect.width - offset; y = rect.top + rect.height / 2 - tipRect.height / 2; } if (chosen === 'right') { x = rect.right + offset; y = rect.top + rect.height / 2 - tipRect.height / 2; } // Innerhalb des Viewports halten (einfaches Clamping) x = clamp(x, 4, vw - tipRect.width - 4); y = clamp(y, 4, vh - tipRect.height - 4); tooltipEl.style.left = `${Math.round(x)}px`; tooltipEl.style.top = `${Math.round(y)}px`; tooltipEl.dataset.placement = chosen; } // ===== Anzeigen/Verbergen ===== function showTooltip(cfg, target) { if (!cfg || !target) return; // Inhalt setzen contentEl.innerHTML = cfg.content || ''; // ARIA-Verknüpfung target.setAttribute('aria-describedby', 'vtp-active-tooltip'); tooltipEl.id = 'vtp-active-tooltip'; tooltipEl.setAttribute('aria-hidden', 'false'); // Sichtbar machen + positionieren tooltipEl.dataset.show = 'true'; current = { key: cfg.key, target, placement: cfg.placement || 'top' }; // Erst nach Render messen requestAnimationFrame(() => { placeTooltip(target, current.placement, cfg.offset || 8); }); } function hideTooltip() { tooltipEl.dataset.show = 'false'; tooltipEl.setAttribute('aria-hidden', 'true'); if (current.target) current.target.removeAttribute('aria-describedby'); current = { key: null, target: null, placement: 'top' }; } // ===== Delegierte Events (Hover + Fokus) ===== function findMatch(startEl, cfg) { // 1) Falls ein präziser Selektor gegeben wurde, daran hochlaufen if (cfg.selector) { return startEl.closest(cfg.selector); } // 2) Sonst zum Widget-Container (#id) hochlaufen if (cfg.widgetId) { const sel = `#${CSS.escape(cfg.widgetId)}`; return startEl.closest(sel); } return null; } $doc.on('pointerover focusin', (e) => { const start = e.target; for (const cfg of registry.values()) { const target = findMatch(start, cfg); if (target) { target.setAttribute('data-vtp-target', ''); showTooltip(cfg, target); return; // ersten Treffer nehmen } } }); // Verbergen: nur wenn wir wirklich das Ziel verlassen haben $doc.on('pointerout', (e) => { if (!current.target) return; // Wenn wir in ein Kind des aktuellen Targets gehen, NICHT schließen if (e.relatedTarget && current.target.contains(e.relatedTarget)) return; // Wenn das Event vom aktuellen Target oder dessen Kind kommt, schließen if (current.target === e.target || current.target.contains(e.target)) { hideTooltip(); } }); // Fokus verliert -> schließen (wie gehabt) $doc.on('focusout', (e) => { if (current.target && (e.target === current.target || current.target.contains(e.target))) { hideTooltip(); } }); // Scroll/Resize -> neu positionieren window.addEventListener('scroll', () => { if (current.target) placeTooltip(current.target, current.placement); }, true); window.addEventListener('resize', () => { if (current.target) placeTooltip(current.target, current.placement); }); // ===== MutationObserver: spätere Widgets ===== function ensureObserver() { if (observer) return; observer = new MutationObserver(() => { // kein explizites Rebinding nötig, Delegation reicht; // aber man könnte hier heuristiken einbauen, falls benötigt }); observer.observe(document.body, { childList: true, subtree: true }); } // ===== Public API ===== const API = { /** * Initialisiert mit einer Liste von Konfigurationen. * cfg: { key, widgetId, selector?, content, placement='top'|'bottom'|'left'|'right'|'auto', offset?, trigger? } * key: eindeutiger Name für spätere Updates/Remove */ init(configs = []) { ensureObserver(); configs.forEach(cfg => this.add(cfg)); }, add(cfg) { if (!cfg || !cfg.key) { console.warn('[Tooltips.add] "key" ist erforderlich.'); return; } if (!cfg.widgetId && !cfg.selector) { console.warn('[Tooltips.add] Entweder "widgetId" oder "selector" ist erforderlich.'); return; } registry.set(cfg.key, { key: cfg.key, widgetId: cfg.widgetId || null, selector: cfg.selector || (cfg.widgetId ? `#${cfg.widgetId}` : null), content: cfg.content || '', placement: cfg.placement || 'top', offset: typeof cfg.offset === 'number' ? cfg.offset : 8 }); }, update(key, patch = {}) { const cur = registry.get(key); if (!cur) return console.warn(`[Tooltips.update] Unbekannter key "${key}".`); registry.set(key, { ...cur, ...patch }); // Wenn der gerade sichtbare Tooltip davon betroffen ist -> sofort aktualisieren if (current.key === key && current.target) { contentEl.innerHTML = registry.get(key).content || ''; placeTooltip(current.target, registry.get(key).placement || 'top', registry.get(key).offset || 8); } }, remove(key) { const cur = registry.get(key); if (!cur) return; if (current.key === key) hideTooltip(); registry.delete(key); }, clear() { hideTooltip(); registry.clear(); }, destroy() { this.clear(); if (observer) observer.disconnect(); observer = null; tooltipEl.remove(); document.getElementById(STYLE_ID)?.remove(); $doc.off('mouseenter focusin mouseleave focusout keydown'); window.removeEventListener('scroll', placeTooltip, true); window.removeEventListener('resize', placeTooltip); } }; // Exponieren window.Tooltips = API; })();
Tooltips.init([ // Einfach: Tooltip an das Element mit ID "widgetA" { key: 'wA', widgetId: 'w000000', content: 'Ich bin ein Tooltip', placement: 'top' } ]);
-
@oliverio
Wahnsinn, da leg ich mir mal als Erinnerung ein Lesezeichen her. hahaFür jemanden, der nicht aus der IT ist wie mich ist das einfach nur faszinierend.
Ich schmökere mich ja aktuell auf W3Schools durch, um etwas mehr in HTML und Co.
reinzustolpern bzw. mich etwas besser in VIS2 zurechtzufinden und mich auszuprobieren.Da komm ich mir wie ein Kindergartenkind vor, wenn ich den Profis zusehe, sozusagen. ^^
-
ich selbst war zu faul, sowas von grund auf aufzubauen
die ehre liegt hier bei chatgpt.
4 prompts und das war funktionsfähig inklusive dokumentation
1 prompt spezifikation
1 prompt nachfrage zu einem detail
1 prompt fehlerbehebung
1 prompt dokumentationwie an anderer stelle schonmal geschrieben muss man die KI aktuell noch leiten. dazu benötigt man etwas wissen.
aber auch Anfänger können da schon gut mit lernen.
wenn man das was die KI ausgibt zu kompliziert oder unvertändlich ist einfach ELI5 (explain like i am 5) hinzufügendas war meine erster prompt
ich habe einen widgets editor welcher einen separaten javascript tab hat. leidr besitzen die widgets keine tooltip möglichkeit. die widgets werden beim laden der seite dynamisch erzeugt, so das man nicht ganz exakt weiß in welcher reihenfolge und zu welchem zeitpunkt ein widget existiert. die standardbibliothek jquery ist vorgeladen. kein jquery-ui ich möchte nun ohne zusätzliche bibliotheken ein javascript erzeugen, über das ich einfach durch konfiguration tooltips hinzufügen kann. jedes widget besitzt eine id. ein widget kann aber uU aus verschachteltem html bestehen, so das uU über die id erweiterten css selektor ich das konkrete element angeben kann über das ein tooltip erscheinen soll. evtl kann man auch angeben ob der tooltip drüber,drunter,rechts,links oder rechts erscheinen soll.
zum lernen könntest du nun den code oben in chatgpt hinzufügen und die das erklären lassen. das können KIs sehr gut
-
@oliverio
Genial.
Danke für den Tipp. -
@cyberraph sagte in Mit Maus über ein Widget und Info Text anzeigen lassen:
Ich schmökere mich ja aktuell auf W3Schools durch
hier noch sehr gute tips fürs lernen
https://wiki.selfhtml.org/ für html css und js
https://developer.mozilla.org/de/ sprachreferenz für javascript
https://javascript.info/ für js, gute grundlagen bis wirklich ins tiefe innere von js -
@oliverio
Danke, leg ich mir in meine Lesezeichen.Ich muss wohl mal kündigen und nur noch meinen Hobbies nachgehen.
Leider braucht man das blöde Geld. Hätt ich doch was gscheites gelernt.