Ein reiner CSS-Galerie-Fokus-Effekt mit :nicht

Avatar of Krzysztof Gonciarz
Krzysztof Gonciarz am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

In der Vergangenheit musste ich oft herausfinden, wie ich Stile auf alle Elemente innerhalb eines Containers anwenden kann, aber nicht auf das Element, über das gerade die Maus fährt.

Animated GIF of a mouse cursor hovering over elements. The element the mouse cursor enters remains visible and the other elements fade.
Demo des erwarteten „Fade-out“-Effekts auf Geschwisterelemente, um Benutzern das „Fokussieren“ auf ein bestimmtes Element zu ermöglichen.

Dieser Effekt erfordert die Auswahl der Geschwister eines gehoverten Elements. Früher habe ich dafür JavaScript verwendet, indem ich Klassen hinzugefügt oder entfernt habe, die die richtigen CSS-Regeln bei mouseenter und mouseleave-Ereignissen definierten, ähnlich wie hier

Obwohl der Code den Zweck erfüllt, sagte mir mein Bauchgefühl immer, dass es einen reinen CSS-Weg geben muss, um das gleiche Ergebnis zu erzielen. Vor einigen Jahren, als ich an einem bestimmten Slider für mein Unternehmen arbeitete, kam ich auf eine Lösung, die der von Chris Geelhoed ähnelt, der die berühmte Netflix-Homepage-Animation nachgebildet hat, und mir wurde klar, dass ich dafür kein JavaScript mehr brauche.

Vor ein paar Monaten versuchte ich, denselben Ansatz auf einem gitterbasierten Feed auf der Website meines Unternehmens anzuwenden, und – zack – es funktionierte wegen des Abstands zwischen den Elementen nicht!

Glücklicherweise stellte sich heraus, dass das nicht so bleiben muss, und wieder brauchte ich dafür kein JavaScript.

Markup und Basis-CSS

Beginnen wir mit der Programmierung, indem wir das entsprechende Markup vorbereiten

  • .grid ist eine gitterbasierte <ul>-Liste;
  • und .grid__child-Elemente sind <li>-Kinder, mit denen wir interagieren möchten.

Das Markup sieht so aus

<ul class="grid">
  <li class="grid__child"></li>
  <li class="grid__child"></li>
  <li class="grid__child"></li>
</ul>

Der Stil sollte so aussehen

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, 15rem);
  grid-gap: 1rem;
}

.grid__child {
  background: rgba(0, 0, 0, .1);
  border-radius: .5rem;
  aspect-ratio: 1/1;
}

Dieser Beispielcode erstellt drei Listenelemente, die drei Spalten in einem Raster belegen.

Die Macht von CSS-Selektoren

Lassen Sie uns nun etwas Interaktivität hinzufügen. Der Ansatz, den ich ursprünglich verfolgte, basierte auf zwei Schritten

  1. Das Hovern über den Container soll die Stile aller darin enthaltenen Elemente ändern…  
  2. …mit Ausnahme desjenigen, über dem sich der Cursor gerade befindet.

Beginnen wir damit, jedes Kind zu erfassen, während sich der Cursor über dem Container befindet

.grid:hover .grid__child {
  /* ... */
}

Zweitens schließen wir das aktuell gehoverte Element aus und reduzieren die opacity aller anderen Kinder

.grid:hover .grid__child:not(:hover) {
  opacity: 0.3;
}

Und das wäre absolut ausreichend für Container ohne Lücken zwischen den Kindelementen

Animated GIF of a mouse cursor interacting with elements that are not separated by any gaps.
Demo einer Lösung, die ohne Lücken funktioniert.

In meinem Fall konnte ich diese Lücken jedoch nicht entfernen

Animated GIF of a mouse cursor hovering over elements. However, when the mouse enters a gap between two elements, the effect ends as the mouse leaves the element.
Demo des Problems, das bei Einführung von Lücken auftritt.

Als ich die Maus zwischen den Kacheln bewegte, verblassten alle Kinderelemente.

Lücken ignorieren

Wir können davon ausgehen, dass Lücken Teile des Containers sind, die nicht von seinen Kindern überlagert werden. Wir möchten den Effekt nicht jedes Mal ausführen, wenn der Cursor in den Container fährt, sondern wenn er über eines der Elemente darin fährt. Können wir die Mausbewegung über den Lücken ignorieren? 

Ja, das können wir, indem wir pointer-events: none auf dem .grid-Container verwenden und sie mit pointer-events: auto auf seinen Kindern wiederherstellen

.grid {
  /* ... */
  pointer-events: none;
}

/* ... */

.grid__child {
  /* ... */
  pointer-events: auto;
}

Fügen wir einfach eine coole Übergangsanimation für die Opazität hinzu und wir haben eine fertige Komponente

Es ist wahrscheinlich noch cooler, wenn wir mehr Kacheln hinzufügen und ein zweidimensionales Layout erstellen

Das endgültige CSS sieht so aus

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, 15rem);
  grid-gap: 3rem;
  pointer-events: none;
}

.grid:hover .grid__child:not(:hover) {
  opacity: 0.3;
}

.grid__child {
  background: rgba(0, 0, 0, .1);
  border-radius: .5rem;
  aspect-ratio: 1/1;
  pointer-events: auto;
  transition: opacity 300ms;
}

Mit nur 2 zusätzlichen Codezeilen haben wir das Lückenproblem gelöst!

Mögliche Probleme

Obwohl es eine kompakte Lösung ist, gibt es einige Situationen, in denen sie möglicherweise einige Workarounds erfordert.

Leider funktioniert dieser Trick nicht, wenn der Container scrollbar sein soll, z. B. in einer Art horizontalem Slider. Der Stil pointer-events: none würde nicht nur das Hover-Ereignis, sondern auch alle anderen ignorieren. In solchen Fällen können Sie den .grid in einen anderen Container einpacken, so

<div class="container">
  <ul class="grid">
    <li class="grid__child"></li>
    <li class="grid__child"></li>
    <li class="grid__child"></li>
    <li class="grid__child"></li>
    <li class="grid__child"></li>
    <li class="grid__child"></li>
    <li class="grid__child"></li>
  </ul>
</div>

Zusammenfassung

Ich ermutige Sie dringend, zu experimentieren und zu versuchen, einen einfacheren und nativeren Ansatz für Aufgaben zu finden, die normalerweise ein gewisses Maß an Komplexität erwarten lassen. Webtechnologien wie CSS werden immer leistungsfähiger und durch die Verwendung nativer Standardlösungen können Sie großartige Ergebnisse erzielen, ohne Ihren Code warten und ihn an Browserhersteller abgeben zu müssen.

Ich hoffe, Ihnen hat dieses kurze Tutorial gefallen und Sie fanden es nützlich. Danke!

Der Autor wählte den Tech Education Fund aus, um im Rahmen des Write for DOnations-Programms eine Spende zu erhalten.