SnippetUI

Popover-Menü

Ein Popover-Menü, das beim Klicken (oder Fokussieren) geöffnet wird und eine Liste von Aktionen anzeigt, nicht beim Bewegen des Mauszeigers.

HTMLCSSJavaScript

Popover-Menüs öffnen sich im Gegensatz zu Tooltips, die beim Bewegen des Mauszeigers angezeigt werden, beim Klicken und werden verwendet, um dem Benutzer eine Liste von Aktionen (z. B. Bearbeiten oder Löschen) anzuzeigen. Es wurde unter Berücksichtigung der Barrierefreiheit entwickelt, z. B. Fokusverwaltung und Verarbeitung ohne Klick-Schließen.

Vorschau

Anwendungsfälle und Funktionen

  • Optionen anzeigen: Ideal für Benutzeroberflächen, die durch Klicken auf ein Menüsymbol (…) oder eine Schaltfläche verschiedene Aktionen (Bearbeiten, Löschen, Teilen usw.) anzeigen.

  • Überlegungen zur Barrierefreiheit: Verwendet die Attribute „aria-expanded“ und „aria-haspopup“, um das Ausblenden und die Fokusverwaltung mit der Escape-Taste zu unterstützen.

  • Flüssige Animation: Um unnatürliche Anzeigewechsel zu verhindern, haben wir einen Übergang implementiert, der Gleiten in Richtung der Y-Achse und Einblenden kombiniert.

Kernpunkte von Design und UX (Design & UX)

  • Klare Hierarchie: Schatten („box-shadow“) werden verwendet, um ein schwebendes Gefühl zu erzeugen und den Inhalt klar vom Hintergrundinhalt zu trennen.

  • Sicherer Betrieb: Beinhaltet einen Prozess zum Schließen des Menüs durch Drücken eines Bereichs außerhalb des Klickbereichs (Click Outside), wodurch die Bedienung sowohl auf Mobilgeräten als auch auf PCs intuitiv ist.

  • Hervorhebung zerstörerischer Aktionen: Wir verwenden roten Text (Gefahr) für Aktionen, die nicht rückgängig gemacht werden können, wie z. B. das Löschen, um Benutzerfehler zu reduzieren.

So passen Sie es an (Anpassungsanleitung)

  • Wenn Sie die Menübreite ändern möchten, passen Sie bitte die Breite von „.popover-menu“ an.

  • Wenn Sie möchten, dass die Öffnungsrichtung unten rechts oder unten links ist, können Sie dies einfach anpassen, indem Sie die Eigenschaften „transform“ und „left“ von „.popover-menu“ ändern.

Implementierungscode (Implementierung)

Mit dem folgenden Code können Sie ein Popover-Menü in Ihr Projekt einbinden.

<div class="popover-wrapper">

<button class="popover-trigger" aria-haspopup="true" aria-expanded="false" id="popover-btn">

  <span>Aktion</span>

  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">

    <path d="m6 9 6 6 6-6"/>

  </svg>

</button>

<div class="popover-menu" role="menu" aria-labelledby="popover-btn">

  <div class="popover-content">

    <button class="popover-item" role="menuitem">

      <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>

      Bearbeiten

    </button>

    <button class="popover-item" role="menuitem">

      <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><path d="M21 9H3"/><path d="M9 21V9"/></svg>

      Duplikat

    </button>

    <div class="popover-divider"></div>

    <button class="popover-item text-danger" role="menuitem">

      <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>

      löschen

    </button>

  </div>

</div>

</div>



<style>
/* CSS hier platzieren */

.popover-wrapper {

position: relative;

display: inline-block;

}



.popover-trigger {

display: flex;

align-items: center;

gap: 6px;

padding: 8px 16px;

font-size: 14px;

font-weight: 500;

color: #334155;

background-color: #ffffff;

border: 1px solid #cbd5e1;

border-radius: 8px;

cursor: pointer;

transition: all 0.2s ease;

box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);

}



.popover-trigger:hover {

background-color: #f1f5f9;

border-color: #94a3b8;

}



.popover-trigger:focus-visible {

outline: 2px solid #3b82f6;

outline-offset: 2px;

}



.popover-trigger svg {

transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);

}



.popover-trigger.is-open svg {

transform: rotate(180deg);

}



.popover-menu {

position: absolute;

top: calc(100% + 8px);

left: 50%;

transform: translateX(-50%) translateY(-10px);

width: 200px;

background: #ffffff;

border-radius: 12px;

box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);

border: 1px solid #e2e8f0;

opacity: 0;

visibility: hidden;

transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);

z-index: 50;

pointer-events: none;

}



.popover-wrapper.is-active .popover-menu {

opacity: 1;

visibility: visible;

transform: translateX(-50%) translateY(0);

pointer-events: auto;

}



.popover-content {

padding: 6px;

display: flex;

flex-direction: column;

gap: 2px;

}



.popover-item {

display: flex;

align-items: center;

gap: 10px;

width: 100%;

padding: 8px 12px;

font-size: 14px;

color: #475569;

background: transparent;

border: none;

border-radius: 6px;

cursor: pointer;

text-align: left;

transition: background-color 0.15s ease, color 0.15s ease;

}



.popover-item:hover,

.popover-item:focus-visible {

background-color: #f1f5f9;

color: #0f172a;

outline: none;

}



.popover-item.text-danger {

color: #ef4444;

}



.popover-item.text-danger:hover,

.popover-item.text-danger:focus-visible {

background-color: #fef2f2;

color: #dc2626;

}



.popover-divider {

height: 1px;

background-color: #e2e8f0;

margin: 4px 0;

}
</style>



<script>

// JavaScript hier platzieren

document.addEventListener('DOMContentLoaded', () => {

const wrappers = document.querySelectorAll('.popover-wrapper');



wrappers.forEach(wrapper => {

  const trigger = wrapper.querySelector('.popover-trigger');

  const menu = wrapper.querySelector('.popover-menu');

  

  if (!trigger || !menu) return;



  const togglePopover = () => {

    const isActive = wrapper.classList.contains('is-active');

    if (isActive) {

      closePopover();

    } else {

      openPopover();

    }

  };



  const openPopover = () => {

    wrapper.classList.add('is-active');

    trigger.classList.add('is-open');

    trigger.setAttribute('aria-expanded', 'true');

  };



  const closePopover = () => {

    wrapper.classList.remove('is-active');

    trigger.classList.remove('is-open');

    trigger.setAttribute('aria-expanded', 'false');

  };



  trigger.addEventListener('click', togglePopover);



  //Außerhalb des Klickbereichs schließen

  document.addEventListener('click', (e) => {

    if (!wrapper.contains(e.target)) {

      closePopover();

    }

  });



  // Mit Escape-Taste schließen

  document.addEventListener('keydown', (e) => {

    if (e.key === 'Escape' && wrapper.classList.contains('is-active')) {

      closePopover();

      trigger.focus();

    }

  });

});

});

</script>