Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Visualisierung
  4. Mit Maus über ein Widget und Info Text anzeigen lassen

NEWS

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    16
    1
    1.8k

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    891

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    25
    1
    2.1k

Mit Maus über ein Widget und Info Text anzeigen lassen

Geplant Angeheftet Gesperrt Verschoben Visualisierung
9 Beiträge 3 Kommentatoren 445 Aufrufe 4 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • J Offline
    J Offline
    jan_xx
    schrieb am zuletzt editiert von
    #1

    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

    OliverIOO 2 Antworten Letzte Antwort
    0
    • J jan_xx

      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

      OliverIOO Offline
      OliverIOO Offline
      OliverIO
      schrieb am zuletzt editiert von
      #2

      @jan_xx

      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.

      Meine Adapter und Widgets
      TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
      Links im Profil

      J 1 Antwort Letzte Antwort
      0
      • OliverIOO OliverIO

        @jan_xx

        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.

        J Offline
        J Offline
        jan_xx
        schrieb am zuletzt editiert von
        #3

        @oliverio
        Das kann ich leider selber nicht, da kenne ich nicht nicht aus...
        Den Forum Eintrag müsste ich nochmal raussuchen

        1 Antwort Letzte Antwort
        0
        • J jan_xx

          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

          OliverIOO Offline
          OliverIOO Offline
          OliverIO
          schrieb am zuletzt editiert von OliverIO
          #4

          @jan_xx

          Tooltips für widgets in vis1 und vis2 <-- Überschrift für die Suchmaschine

          so eine kleine Sonntag Nachmittag Beschäftigung
          funktioniert in vis1 und vis2

          Der 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'
                  }
              ]);
          
          

          Meine Adapter und Widgets
          TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
          Links im Profil

          CyberraphC 1 Antwort Letzte Antwort
          0
          • OliverIOO OliverIO

            @jan_xx

            Tooltips für widgets in vis1 und vis2 <-- Überschrift für die Suchmaschine

            so eine kleine Sonntag Nachmittag Beschäftigung
            funktioniert in vis1 und vis2

            Der 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'
                    }
                ]);
            
            
            CyberraphC Online
            CyberraphC Online
            Cyberraph
            schrieb am zuletzt editiert von
            #5

            @oliverio
            Wahnsinn, da leg ich mir mal als Erinnerung ein Lesezeichen her. haha

            Fü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. ^^

            Beste Grüße!

            io-Broker Neuling 2024 :-)

            Bislang jedoch einiges an Beiträgen und Grundlagen eingeflößt, um etwas besser empor zu irren.

            OliverIOO 1 Antwort Letzte Antwort
            0
            • OliverIOO Offline
              OliverIOO Offline
              OliverIO
              schrieb am zuletzt editiert von OliverIO
              #6

              @cyberraph

              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 dokumentation

              wie 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ügen

              das 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

              Meine Adapter und Widgets
              TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
              Links im Profil

              CyberraphC 1 Antwort Letzte Antwort
              0
              • OliverIOO OliverIO

                @cyberraph

                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 dokumentation

                wie 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ügen

                das 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

                CyberraphC Online
                CyberraphC Online
                Cyberraph
                schrieb am zuletzt editiert von
                #7

                @oliverio
                Genial.
                Danke für den Tipp. :-)

                Beste Grüße!

                io-Broker Neuling 2024 :-)

                Bislang jedoch einiges an Beiträgen und Grundlagen eingeflößt, um etwas besser empor zu irren.

                1 Antwort Letzte Antwort
                0
                • CyberraphC Cyberraph

                  @oliverio
                  Wahnsinn, da leg ich mir mal als Erinnerung ein Lesezeichen her. haha

                  Fü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. ^^

                  OliverIOO Offline
                  OliverIOO Offline
                  OliverIO
                  schrieb am zuletzt editiert von
                  #8

                  @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

                  Meine Adapter und Widgets
                  TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                  Links im Profil

                  CyberraphC 1 Antwort Letzte Antwort
                  0
                  • OliverIOO OliverIO

                    @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

                    CyberraphC Online
                    CyberraphC Online
                    Cyberraph
                    schrieb am zuletzt editiert von
                    #9

                    @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. :grin:

                    Beste Grüße!

                    io-Broker Neuling 2024 :-)

                    Bislang jedoch einiges an Beiträgen und Grundlagen eingeflößt, um etwas besser empor zu irren.

                    1 Antwort Letzte Antwort
                    0
                    Antworten
                    • In einem neuen Thema antworten
                    Anmelden zum Antworten
                    • Älteste zuerst
                    • Neuste zuerst
                    • Meiste Stimmen


                    Support us

                    ioBroker
                    Community Adapters
                    Donate

                    765

                    Online

                    32.6k

                    Benutzer

                    82.1k

                    Themen

                    1.3m

                    Beiträge
                    Community
                    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                    ioBroker Community 2014-2025
                    logo
                    • Anmelden

                    • Du hast noch kein Konto? Registrieren

                    • Anmelden oder registrieren, um zu suchen
                    • Erster Beitrag
                      Letzter Beitrag
                    0
                    • Home
                    • Aktuell
                    • Tags
                    • Ungelesen 0
                    • Kategorien
                    • Unreplied
                    • Beliebt
                    • GitHub
                    • Docu
                    • Hilfe