Der folgende Artikel stammt von James Nowland, einem Frontend-Entwickler bei Headjam, einer Kreativagentur in Newcastle, Australien. James hat hier einen ziemlich einfachen kleinen Effekt erzielt, der aber vielleicht den Einsatz von etwas JavaScript erfordern würde. Stattdessen nutzt er einige clevere Selektoren.
In diesem Artikel werde ich kreative Wege aufzeigen, wie Geschwister-Selektoren und Pseudo-Elemente verwendet werden können, um eine reine CSS-Menüanzeige zu erstellen, die normalerweise mit JavaScript realisiert werden würde.
Hier ist, was wir machen werden
Siehe den Pen Schritt 3 von CSS-Tricks (@css-tricks) auf CodePen.
Wir werden dies in drei Schritte unterteilen
- Grundlegende Struktur und Styling
- Aufbau der Anzeige
- Bewegung der Anzeige
Wir werden auch SCSS verwenden, um von den Variablen und Funktionen zu profitieren, die Sass bietet und die die Dinge auf lange Sicht deutlich einfacher zu warten machen.
Schritt 1: Grundlegende Struktur und Styling
Zuerst richten wir das HTML für das Menü mit einer einfachen ungeordneten Listenstruktur ein. Wir können auch die grundlegenden Klassennamen markieren, um loszulegen.
<ul class="PrimaryNav">
<li class="Nav-item">Home</li>
<li class="Nav-item">About</li>
<li class="Nav-item is-active">Writing</li>
<li class="Nav-item">Clients</li>
<li class="Nav-item">Contact</li>
</ul>
Bisher nichts Besonderes. Wir haben das <ul>-Element mit dem Klassennamen PrimaryNav, das als Container für die darin enthaltenen Listenelemente dient, die jeweils die Klasse Nav-item haben.
Definition der Variablen
Eines der Hauptmerkmale dieser Navigation ist eine maximale Breite, die den Raum eines Containers basierend auf der Anzahl der darin enthaltenen Menüeinträge füllt. In diesem Fall legen wir in unserem SCSS eine Variable $menu-items fest, die dann zur Berechnung des $width-Werts jedes .Nav-item im Markup verwendet wird.
Wir haben auch eine Variable $indicator-color hinzugefügt, um – Sie ahnen es – die Farbe zu definieren, die für die Hover-Anzeige des Menüs verwendet wird.
// Menu Item Variables
// The number of items in the menu
$menu-items: 5;
// We multiply it by 1% to get the correct % unit
$width: (100/$menu-items) * 1%;
// Colors
$background-color: #121212;
$indicator-color: #e82d00;
Styling der Elemente
Von hier aus können wir die grundlegenden Stile für das Menü erstellen
// The parent container
.PrimaryNav {
// Remove the bullet points by default
list-style: none;
// Center all the things!
margin: 50px auto;
// The nav will never exceed this width and what our calculated percentages related back to
max-width: 720px;
padding: 0;
width: 100%;
}
// The menu items
.Nav-item {
background: #fff;
display: block;
float: left;
margin: 0;
padding: 0;
text-align: center;
// Our current calculation of 5 items will generate 20%
width: $width;
// The first item in the menu
&:first-child {
border-radius: 3px 0 0 3px;
}
// The last item in the menu
&:last-child {
border-radius: 0 3px 3px 0;
}
// If the menu item is active, give it the same color as the indicator
&.is-active a {
color: $indicator-color;
}
a {
color: $background-color;
display: block;
padding-top: 20px;
padding-bottom: 20px;
text-decoration: none;
&:hover {
color: $indicator-color;
}
}
}
Siehe den Pen Schritt 1 von CSS-Tricks (@css-tricks) auf CodePen.
Schritt 2: Aufbau der Anzeige
Wir werden dies so markieren, dass mehrere Klassen verwendet werden. Wir könnten dasselbe mit nur der Klasse .PrimaryNav erreichen, aber das Hinzufügen einer weiteren Klasse wird zukünftig mehr Flexibilität ermöglichen.
Wir haben bereits die Klasse .PrimaryNav, die das Hauptnavigationsstyling enthält. Jetzt erstellen wir .with-indicator, um die Anzeige zu erstellen.
<ul class="PrimaryNav with-indicator">
</ul>
Hier können wir CSS anstelle dessen verwenden, was wir normalerweise in JavaScript erledigen würden. Wir wissen, dass das Hinzufügen einer Klasse zu einem Element beim Hovern in den Bereich von JavaScript fällt, aber sehen wir, wie wir das allein mit CSS machen können.
Das Schwierige ist, die Menüeinträge miteinander kommunizieren zu lassen. In einer ungeordneten Liste kann das erste Listenelement (:first-child) mit dem zweiten Kind über den Geschwister-Selektor + oder ~ sprechen, aber das zweite Listenelement kann nicht mit dem ersten Kind sprechen (in CSS kann man nicht rückwärts im DOM navigieren).
Siehe den Pen Schritt 2 von CSS-Tricks (@css-tricks) auf CodePen.
Es stellt sich heraus, dass der beste Listener unter den Listenelementen das :last-child ist. Das letzte Kind kann **alle** :hover- und :active-Zustände seiner Geschwister hören. Das macht es zum perfekten Kandidaten, um die Anzeige zu platzieren.
Wir erstellen die rote Anzeige mit den Elementen :before und :after des letzten Kindes. Das Element :before verwendet ein CSS-Dreieck und einen negativen Rand, um es zu zentrieren.
// The hover indicator
.with-indicator {
// The menu is "relative" to the absolute position last-child pseudo elements.
position: relative;
.Nav-item:last-child {
&:before, &:after {
content: '';
display: block;
position: absolute;
}
// The CSS Triangle
&:before {
width: 0;
height: 0;
border: 6px solid transparent;
border-top-color: $color-indicator;
top: 0;
left: 12.5%;
// Fix the offset - may vary per use
margin-left: -3px;
}
// The block that sits behind the text
&:after {
width: $width;
background: $indicator-color;
top: -6px;
bottom: -6px;
left: 0;
z-index: -1;
}
}
}
Schritt 3: Bewegung der Anzeige
Nun, da die Anzeige eingerichtet ist, muss sie sich bewegen können, wenn der Cursor über Menüeinträgen schwebt. Hier zeigt sich die Kraft des ~-Selektors, der verwendet wird, um alle Elemente zwischen dem ersten und dem letzten Kind im Markup abzugleichen.
Momentan ist position:relative standardmäßig auf das <ul>-Element gesetzt, was bedeutet, dass die Anzeige bündig mit dem ersten Element sitzt. Wir können die Anzeige von Element zu Element verschieben, indem wir die left-Position ändern und – da alle Menüs die gleiche Breite haben – wissen wir, dass wir, um sie um eine Stelle nach unten zu verschieben, die :last-child-Selektoren für :before und :after einen Versatz haben müssen, der der Breite eines .Nav-item entspricht. Erinnern Sie sich an unsere praktische $width-Variable? Die können wir auch für das left-Attribut verwenden.
So würden wir das in vanilla CSS einrichten
.with-indicator .Nav-item:nth-child(1).is-active ~ .Nav-item:last-child:after {
left: 0;
}
.with-indicator .Nav-item:nth-child(2).is-active ~ .Nav-item:last-child:after {
left: 20%;
}
.with-indicator .Nav-item:nth-child(3).is-active ~ .Nav-item:last-child:after {
left: 40%;
}
.with-indicator .Nav-item:nth-child(4).is-active:after {
left: 60%;
}
.with-indicator .Nav-item:nth-child(5).is-active:after {
left: 80%;
}
Machen wir es dynamisch mit Sass
// Menu Item Variables
// The number of items in the menu, plus one for offset
$menu-items: 5;
// The actual number of items in the menu
$menu-items-loop-offset: $menu-items - 1;
// We multiply it by 1% to get the correct % unit
$width: (100/$menu-items) * 1%;
.with-indicator {
@for $i from 1 through $menu-items-loop-offset {
// When the .Nav-item is active, make the indicator line up with the navigation item.
.Nav-item:nth-child(#{$i}).is-active ~ .Nav-item:last-child:after {
left:($width*$i)-$width;
}
.Nav-item:nth-child(#{$i}).is-active ~ .Nav-item:last-child:before {
left:($width*$i)+($width/2)-$width; /* this ensures the triangle lines up to the menu. */
}
} // end @for loop
Es ist erwähnenswert, dass das Dreieck :before zusätzlich zu diesem left-Offset einen halben Breiten-Offset hat.
Fügen wir nun eine Animation und eine weitere Sass for-Schleife hinzu, damit wir initialisieren können, wo sich die Anzeige befindet, basierend auf der aktuellen Seite. Wenn Sie mit der Maus über das Element fahren, bewegt sich die Anzeige. Sobald Sie aber mit der Maus herausfahren, kehrt sie zum Zustand is-active zurück. Eine schöne und saubere Möglichkeit, eine Menüanzeige ohne JavaScript zu erstellen.
// We had to use !important to make the hovers overide for when the :last-child is-active or hovered
@for $i from 1 through $menu-items-loop-offset {
// When the menu is :hover make the indicator line up with it.
.Nav-item:nth-child(#{$i}):hover ~ .Nav-item:last-child:after {
left:($width*$i)-$width !important;
}
.Nav-item:nth-child(#{$i}):hover ~ .Nav-item:last-child:before{
left:($width*$i)+($width/2)-$width !important;
}
} // end @for loop
// make sure the last-child talks to itself
.Nav-item {
&:last-child {
&:hover, &.is-active {
&:before {
left: (100%-$width)+($width/2) !important;
}
&:after{
left: 100%-$width !important;
}
}
}
}
Das Endergebnis
Und da haben wir es! Eine animierte Menüanzeige ohne JavaScript-Abhängigkeit.
Siehe den Pen Schritt 3 von CSS-Tricks (@css-tricks) auf CodePen.
Ziemlich cooler Effekt; und ein gutes Beispiel dafür, wie Sass verwendet werden kann.
Ich habe vor einiger Zeit etwas Ähnliches gemacht, aber es ist nicht so schön wie Ihres und verwendet ein hässliches, nicht semantisches Element zur Erstellung der Anzeige, daher gefällt mir Ihre Methode viel besser :)
Gute Arbeit und schöne Zusammenfassung.
Zeigt, wie Sass effektiv eingesetzt werden kann, anyway, es scheint ein Gastbeitrag auf css-tricks zu sein.
way2sms
Ich habe es ausprobiert, und
...
&:after {
...
bottom: -6px;
...
}
...
hat nicht wie geplant funktioniert, es hat nur funktioniert, als ich es auf -66px gesetzt habe.
Ich weiß nicht warum? (Habe es in Chrome und Firefox ausprobiert)
Meiner Meinung nach fehlt ein Clearfix.
Danke, Dave, es gibt globale Resets, die hier nicht gezeigt werden und deshalb hat es nicht richtig funktioniert.
Das funktioniert bei mir im neuesten Chrome auf einem iPad mit iOS 9 nicht. Ich muss jedes Mal zum Kontakt-Tab zurückkehren und dann einen anderen Tab anklicken, um einen Übergang zu sehen.
Irgendwelche Ideen, wie man es für unterschiedlich breite Menüeinträge funktionieren lässt?
Es ist weitaus einfacher, dies mit festen Prozentsätzen zu tun, wie in der Demo gezeigt, und es ist responsiver.
Wenn Sie die exakten Breiten all Ihrer Linkelemente **kennen**, könnten Sie theoretisch feste Breiten verwenden, wie z.B.:
Wenn Sie jedoch viele Elemente so fest codieren würden, würden Sie sich vor jedem Update fürchten.
Ich persönlich würde JavaScript in Betracht ziehen, wenn es viele unterschiedlich breite Navigationspunkte gäbe.
Schön, aber wenn Sie die Anzeige-Klasse
.is-activeverwenden, müssen Sie ein kleines bisschen JavaScript verwenden, um die Klasse hinzuzufügen (oder den CSS-Selektor:active), nicht wahr? Gibt es da Magie? DankeDie Klasse
is-activekann vom Server beim Laden der Seite gesetzt werden. Da diese Demo die Seiten nicht neu lädt, ist es weniger offensichtlich.Es gibt verschiedene Möglichkeiten, dies in WordPress zu erreichen, aber eine einfache ist die Verwendung von is_page wie unten gezeigt.
Alternativ, wenn Sie ein anderes CMS wie Craft verwenden, können Sie etwas Ähnliches tun:
Oder sogar ein Makro erstellen.
Ich hoffe, das hilft!
Wirklich schöner Trick, aber wenn die Klasse
.is-activeauf dem letzten Element liegt, scheint sie nicht richtig zu animieren. Ich konnte keine Möglichkeit finden, dies zu beheben, und es ist vielleicht nur mein Computer…? Ich habe es mit dem Original-Pen in diesem Beitrag getestet (Link), aber mit dem HTML wie folgt:Trotzdem, liebe den Beitrag :-)
Danke, das Problem, dass das letzte Element nicht richtig animiert, liegt daran, dass
:last-childin Bezug auf die Spezifität gegenüber dem Geschwister-Selektor nicht stark genug ist.Eine Möglichkeit, dies zu beheben, ist die Verwendung von
!importantfür die Selektoren. Hier ist ein aktualisierter Pen, bei dem dies funktioniert.Ich hoffte, eine Möglichkeit zu finden, dies zu beheben, ohne auf die letzte Option
!importantzurückgreifen zu müssen.Ich habe Chris gebeten, den Artikel mit den Änderungen anzupassen :)
Sie sind mein neuer Gott!
Für ein vertikales Beispiel: http://cssmojo.com/menus-with-a-sliding-marker/
Die Verwendung von :hover ist der einzige Weg, wie dies rein mit CSS möglich ist, und meiner Meinung nach ist es ein bisschen zu umständlich/verschwendet als Hover-Effekt. Es wird auf Touch-Geräten keinen Nutzen bringen und kann jankiness hervorheben, die aus einer Vielzahl von Gründen vorhanden sein kann.
Unabhängig davon sollte es sparsam eingesetzt werden, wenn Sie mehr als eine einfache statische Website planen oder wenn die Tabs/Navs dynamisch und nicht vertikal sind.
Dieser Effekt eignet sich am besten als animierte Anzeige für einen aktiven Tab/Nav, wobei die gleiche Technik in Verbindung mit der .active-Klasse eines beliebigen Out-of-the-Box-Tab-Steuerelements verwendet wird. Wenn Sie wissen, dass sich die Navbar nicht ändert, ist es ein sehr guter Effekt für eine SPA oder eine Landingpage in Kombination mit Scrollspy.
Ein Vorbehalt bei dieser Technik (wenn horizontal) ist, dass alle Listenelemente gleich groß sein müssen. Das macht es nicht ganz einfach, wenn Sie planen, eine dynamische Website zu erstellen, bei der Tabs in eine neue Zeile umbrechen könnten, und ruiniert die Translate-Berechnungen… das fügt nur unnötige Komplexität hinzu.
Polymer hat Elemente, die variable Längen mit Tab-Scrolling ermöglichen, aber das zeigt, wie technisch es ist, es selbst robust zu implementieren.
https://elements.polymer-project.org/elements/paper-tabs?view=demo:demo/index.html&active=paper-tabs
100% einverstanden! Es ist eher ein CSS-Trick… sehen Sie, was ich dort gemacht habe :|
Coole Sache! Eine Sache jedoch: Wenn ich über die Anzeige schwebe (nur die roten Linien, entweder die obere oder die untere), springt sie ganz zum letzten Kind (Opera, Chrome, Firefox, IE11).
Danke, Andrei, das ist mir nicht aufgefallen, aber wenn man die Anzeige groß macht, wird es sehr offensichtlich.
Dies liegt daran, dass der
:hoverauf der Anzeige tatsächlich auf einem Teil deslast-childliegt.Chris hat ein Video gemacht, das diese Verhaltensweise absichtlich verwendet. Da wir dieses Verhalten nicht wollen – wenn wir hier pointer-events verwenden, z.B.
pointer-events: none;auf:beforeund:after, die die Anzeige bilden, wird dies in den unterstützten Browsern verhindert.Coole Idee, in der Praxis zu sehen. Ich dachte nur, ich gebe Feedback zu etwas, das ich kürzlich herausgefunden habe, um Ihr SASS etwas aufzuräumen.
kann sein
Referenz: https://sass-lang.de/documentation/Sass/Script/Functions.html#percentage-instance_method
Eigentlich müssen Sie keine Dramen veranstalten, es kann leicht erreicht werden mit