Herauspoppen aus verstecktem Überlauf

Avatar of Agop Shirinian
Von Agop Shirinian am

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

Der folgende Beitrag ist ein Gastbeitrag von Agop Shirinian. Agop stieß auf ein interessantes Szenario, bei dem er ein Element benötigte, das in einer Richtung scrollbar sein sollte, während der Überlauf in der anderen Richtung zugelassen werden sollte. Man könnte denken, dafür seien overflow-x und overflow-y da, aber so einfach ist es nicht. Ich überlasse Agop die Erklärung.

Sie haben die Aufgabe, ein scrollbares Menü mit Untermenüs zu erstellen, die beim Überfahren eines übergeordneten Menüpunkts herausspringen.

Einfach!

Erstellen Sie eine Liste für das Menü, fügen Sie einige verschachtelte Listen für die Untermenüs hinzu, positionieren Sie die verschachtelten Listen basierend auf ihren übergeordneten Listenelementen, fertig!

Siehe den Pen Scrollbares Menü mit herausspringenden Untermenüs (kaputt) von Agop (@agop) auf CodePen.

Moment, das stimmt nicht. Oh, natürlich haben wir overflow: auto verwendet – vielleicht, wenn wir overflow-x: visible verwenden, wird der horizontale Überlauf der Untermenüs sichtbar sein?

Siehe den Pen Scrollbares Menü mit herausspringenden Untermenüs (kaputt #2) von Agop (@agop) auf CodePen.

Was ist los? Warum haben wir immer noch Scrollbalken?

Das Problem

Wenn wir uns die W3C-Spezifikation ansehen, finden wir folgende Erklärung:

Die berechneten Werte von 'overflow-x' und 'overflow-y' sind dieselben wie ihre spezifizierten Werte, mit der Ausnahme, dass einige Kombinationen mit 'visible' nicht möglich sind: Wenn einer als 'visible' spezifiziert ist und der andere 'scroll' oder 'auto' ist, dann wird 'visible' auf 'auto' gesetzt.

Im Grunde wird das hier

overflow-x: visible;
overflow-y: auto;

Zu diesem hier

overflow-x: auto;
overflow-y: auto;

Wir können also keinen sichtbaren horizontalen Überlauf haben, wenn der vertikale Überlauf unsichtbar ist, und umgekehrt.

Und wenn wir keinen sichtbaren horizontalen Überlauf haben können, können wir unsere herausspringenden Untermenüs nicht haben!

Die Lösung

Interessanterweise, wenn wir position: relative von den Menüpunkten weglassen, erscheinen die Untermenüs trotzdem, positioniert basierend auf ihrem nächstgelegenen positionierten Vorfahren. In diesem Fall haben sie keinen positionierten Vorfahren, also sind sie relativ zum <body> positioniert

Siehe den Pen Scrollbares Menü mit herausspringenden Untermenüs (Schritt 1) von Agop (@agop) auf CodePen.

Im Grunde muss in einem Element mit overflow: hidden der nächstgelegene positionierte Vorfahre eines absolut positionierten Elements auch ein Vorfahre des Elements mit overflow: hidden sein, damit es außerhalb dessen Grenzen erscheint.

Wenn wir das wissen, können wir einen Wrapper um die Menüs hinzufügen, der als nächstgelegener positionierter Vorfahre für jedes Untermenü fungiert. Dann können wir die Untermenü-Wrapper mit ein wenig JavaScript positionieren, wann immer der Benutzer über einen Menüpunkt fährt.

Siehe den Pen Scrollbares Menü mit herausspringenden Untermenüs von Agop (@agop) auf CodePen.

Und das war's! Da weder die Menüs noch die Menüpunkte positioniert sind, können die Untermenüs aus dem versteckten/scrollbaren Überlauf herausspringen. Jetzt können wir so viele Ebenen verschachtelter Untermenüs haben, wie wir wollen, und wir werden keine unerwünschten Clipping-Effekte haben.

Fazit

Leider ist diese Methode, Elemente anzuzeigen, die sonst verborgen wären, sehr obskur.

Es wäre schön, wenn wir eine Clip-Tiefe angeben könnten, die steuert, welcher Vorfahre in der Hierarchie für das Clipping eines bestimmten Elements verantwortlich wäre.

./* Fair warning: not real code */
.submenu {
  /* only an ancestor 2 levels up can clip this element */ 
  clip-depth: 2;
}

Oder, noch besser, vielleicht könnten wir den Clipping-Vorfahren per CSS-Selektor angeben.

/* Fair warning: not real code */
.submenu {
  /* only an ancestor that matches the .panel selector can clip this element */
  clip-parent: .panel;
}