5202

ein Blog über technische Fragen zu Blogger

Einfache Dropdown-Liste

von
Ihr möchtet in eurem minimalistischem Layout kein ausgewachsenes Menü, aber trotzdem eine kleine Navigation haben? Ihr habt zuwenig Platz unterm Header? Ihr wollt ein noch reduzierteres Layout bauen?

Für alle solche Fälle könntet ihr euch mal meine Dropdown Liste anschauen - sozusagen die kleine Schwester von einem 'richtigem' Dropdown Menü.

proof of concept

Ich habe die Dropdown Liste mit reiner CSS in zwei Versionen gebaut - einmal mit einer transition [Demo1] und einmal mit einer keyframes animation [Demo2]. Beide Versionen laufen in allen richtigen Browsern und im IE ab Version 8+.

Beide Konzepte würden in Blogger Blogs prinzipiell funktionieren, wenn eine entsprechende Anpassung in der CSS gemacht wird. In der jetzigen Fassung ist das lediglich ein allgemeines Konzept.

Fall von eurer Seite Interesse besteht, würde ich noch einen Nachtrag für Dropdown Listen in Blogger schreiben ... oder ihr versucht euch mal selber dran, ist nicht schwierig!

HTML

Ich wollte etwas sehr, sehr simples bauen - für beide Versionen der Dropdown Liste könnt ihr die gleiche HTML benützen.

Fangen wir mit einer unsortierten Liste in einem <div> Container an:
<div class="droplist">
    <ul>
  <li><span>Text1</span></li>
  <li><span>Text2</span></li>
  <li><span>Text3</span></li>
  <li><span>text4</span></li>
 </ul>
</div>
Der Inhalt der Listenpunkte sind in dem Fall <span> Container - es gingen natürlich auch Ankerelemente mit Links.

Als 'Trigger' zum klicken dient ein Input-Element mit dazugehörigem Label:
<div class="droplist">
 <input id="trigger" type="checkbox">
    <label for="trigger">click me!</label>
    <ul>
  <li><span>Text1</span></li>
  <li><span>Text2</span></li>
  <li><span>Text3</span></li>
  <li><span>text4</span></li>
 </ul>
</div>
An HTML ist das alles - mehr braucht ihr nicht.

checkbox Hack für mobile Browser

Damit ihr ein ein input Element auch auf mobilen Browsern benützen könnt, braucht ihr einen kleinen Hack.

Mit dieser CSS wird das input Element auf 100% Breite und Höhe des umschließenden Containers gebracht - mit dem z-Index von 13 legt ihr es über alle anderen Elemente - mit opacity=0 wird das Element unsichtbar, ist aber nach wie vor vorhanden:
.droplist input {
        position: absolute;
        width: 100%;
        height: 100%;
        left: 0;
        top: 0;
        z-index: 13;
        opacity: 0;
        cursor: pointer;
    }


Durch diesen Hack klickt ihr ab jetzt nicht mehr das Label (das auf mobilen Browsern nicht klickbar ist), sondern das input Element - mission accomplished!

Demo1 mit transition

Ich habe die Skizze mit zwei verschiedenen CSS Techniken geschrieben - warum, dazu später mehr. Fangen wir mit der simpleren Version an.

Zuerst einmal gestalten wir denn 'sichtbaren' oberen Bereich der Liste:
.droplist {
    width: 200px;
    height: 60px;
    position: relative;
    text-align: center;
}
Der Container bekommt eine Höhe, Breite und wird zentriert.

Dann gestalten wir das Label, also den 'click me!' Text:
.droplist label {
        position: relative;
        display: block;
        line-height: 60px;
        z-index: 10;
        background: rgb(255, 255, 255);
    }
Für die vertikale Zentrierung benützen wir einen alten Trick, indem wir den Wert vom line-height gleich der Höhe des Containers setzen.

Der z-index: 10 ist nötig, damit das Label über den Listenpunkte, aber unter dem input Element bleibt - ihr erinnert euch? Das hatte einen z-index von 13.

Die <span> Container in den Listenpunkte bekommen nun eine Höhe und Breite und a bisserl Style:
  .droplist span {
        display: block;
        height: 60px;
        width: 200px;
        line-height: 60px;
        text-align: center;
        box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
        background: rgba(0, 0, 0, 0.01);
    }
Als letztes gestalten wir die Listenpunkte für die _eingeklappte_ Liste:
    .droplist li {
        position: absolute;
        top: 0;
        left: 0;
        list-style: none;
      -webkit-transition: all 300ms ease 0ms;
         -moz-transition: all 300ms ease 0ms;
           -o-transition: all 300ms ease 0ms;
          -ms-transition: all 300ms ease 0ms;
              transition: all 300ms ease 0ms;

    }
Damit kommen alle Listenpunkte unter die oberste Schicht zu liegen - geht, ist aber langweilig.

Damit das a bisserl interessanter und wie eine 'gestapelte' Liste ausschaut, verschieben wir jeden einzelnen Listenpunkt um ein paar Pixel:
 .droplist li:nth-of-type(1) { top: 3px }
    .droplist li:nth-of-type(2) {
        top: 6px;
        left: 2px;
    }
    .droplist li:nth-of-type(3) {
        top: 9px;
        left: 4px;
    }
    .droplist li:nth-of-type(4) {
        top: 12px;
        left: 6px;
    }
Ausklappen
Ausgeklappt wird das Menü, indem die Checkbox geklickt und auf die Pseudoklasse :checked gesetzt wird ... dadurch wird die Positionen der einzelnen Listenpunkte geändert:
input[type="checkbox"]:checked ~ ul li:nth-of-type(1) {
    top: 75px
    }
     input[type="checkbox"]:checked ~ ul li:nth-of-type(2) {
    top: 135px
    }
     input[type="checkbox"]:checked ~ ul li:nth-of-type(3) {
    top: 195px
    }
     input[type="checkbox"]:checked ~ ul li:nth-of-type(4) {
    top: 255px
    }
Jedes Element verschiebt sich dabei um 60px nach unten. Simpel, oder muss ich dazu noch mehr erklären?

Auf die Verschiebung legt ihr noch eine Transition, damit das ganze 'smoother' ausschaut.
  input[type="checkbox"]:checked ~ ul li {
   -webkit-transition: all 300ms ease 0ms;
      -moz-transition: all 300ms ease 0ms;
        -o-transition: all 300ms ease 0ms;
       -ms-transition: all 300ms ease 0ms;
           transition: all 300ms ease 0ms;
Das war's eigentlich schon .. schaut auf dem ersten Blick kompliziert aus, ist aber letztlich sehr einfach.

Demo2 mit keyframes animation

Die 2. Version von dieser Dropdown Liste habe ich mit einem Keyframes Animation gebaut ... ist am Anfang praktisch erst mal gleich zur CSS von der 1. Version:
.droplist {
    width: 200px;
    height: 60px;
    position: relative;
    text-align: center;
    background: yellow;
}
    .droplist input {
        position: absolute;
        width: 100%;
        height: 100%;
        left: 0;
        tab-size: 0;
        z-index: 13;
        opacity: 0;
        cursor: pointer;
    }
    .droplist label {
        position: relative;
        display: block;
        line-height: 60px;
        z-index: 10;
        background: #fff;
    }
    .droplist span {
        display: block;
        height: 60px;
        width: 200px;
        line-height: 60px;
        text-align: center;
        box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
        background: rgba(0, 0, 0, 0.01);
    }
    .droplist li {
        position: absolute;
        top: 0;
        left: 0;
        list-style: none;
    }
    .droplist li:nth-of-type(1) { top: 3px }
    .droplist li:nth-of-type(2) {
        top: 6px;
        left: 2px;
    }
    .droplist li:nth-of-type(3) {
        top: 9px;
        left: 4px;
    }
    .droplist li:nth-of-type(4) {
        top: 12px;
        left: 6px;
    }
Für die Animation müsst ihr zuerst einmal eine Keyframes Funktion definieren - beachtet bitte, das ich alles folgende ohne Vendor Prefix schreibe, die vollständige CSS findet ihr im Download.

Hier die Keyframes Funktion für das erste Listenelement:
@keyframes "move1" { 
 0% {
     transform: translate(0, 0) rotate(0deg);
 }
 100% {
     transform: translate(0, 75px) rotate(4deg);
 }
}
Die Funktion verschiebt das Element um 75px nach unten und verdreht es um 4° nach rechts.

Ausgeführt wird der Spaß über die animate Eigenschaft:
input[type="checkbox"]:checked ~ ul li:nth-of-type(1) {
    animation: move1 .4s ease 1;
    animation-fill-mode: forwards;
}
Auf Deutsch übersetzt steht da:

Ihr führt die Keyframes Funktion move1 in einer Zeitspanne von 1s und mit einer Verzögerung von 0,4s aus. Am Ende stoppt die Animation im letzten Zustand und springt nicht zurück [=animation-fill-mode: forwards].

Alles klar? Als nächstes braucht ihr eine Funktion für den umgekehrten Vorgang, also wenn die Dropdown Liste wieder einklappen soll - als erstes die Keyframes Funktion:
@keyframes "remove1" { 
 0% {
     transform: translate(0, 75px) rotate(4deg);
 }
 100% {
     transform: translate(0, 0) rotate(0deg);
 }
}
Also das gleiche in Grün, nur umgekehrt. Die Animation ist die gleiche wie eben ... aber Moment mal, wie sagen wir einem Browser, das er nicht geklickt ist? Geht simpel mit einer weiteren Pseudoklasse, nämlich :not
input[type="checkbox"]:not(:checked) ~ ul li:nth-of-type(1) {
    animation: remove1 .4s ease 1;
    animation-fill-mode: forwards;
}
Für das zweite Element von oben, schreibt ihr ein zweit Funktion, mit einer Verschiebung von 135px:
@keyframes "move2" { 
 0% {
     transform: translate(0, 0) rotate(0deg);
 }
 100% {
     transform: translate(0, 135px) rotate(3deg);
 }
}
Dazu braucht ihr wieder eine dazugehörige Animation:
input[type="checkbox"]:checked ~ ul li:nth-of-type(2) {
    animation: move2 .4s ease 1;
    animation-fill-mode: forwards;
}
Und eine 'Remove2' Keyframes Animation und so weiter. Wenn ihr das Prinzip dahinter erst mal begriffen habt, dann ist das reine Fleißarbeit. Die Frage bleibt: Warum diese [relativ] komplizierte Keyframes Animation?

Keyframes Animation vs. Transition

Ein bewegtes Element muss vom Browser dargestellt werden ... dazu werden die einzelnen Bewegungsphasen berechnet und nacheinander dargestellt, indem das Bild innerhalb des Browserfensters immer wieder neue aufgebaut wird. Das kennt ihr im Prinzip aus jedem Spiel.

Ihr merkt es vielleicht nicht, aber das alles kostet euch Ressourcen. Eine 2d Transition wird etwa über den CPU berechnet und bei großen Bewegungen von vielen Elementen kann euch durchaus mal eurer Rechner in die Knie gehen.

Keyframes Animation werden dagegen über die GPU [Grafikkarte] berechnet. Die Dinger sind dafür konstruiert, Bewegung darzustellen und in der Regel sind Keyframes viel 'smoother' als eine Transition.
Ich sehe keinen Unterschied ...
Okay, ihr habt die Skizze auf eurem Quadcore Rechenmonster angeschaut und ihr seht keinen Unterschied ... dazu ist die animierte Fläche zu klein. Aber nehmt einmal - falls vorhanden - euer iPad zur Hand und schaut euch meine Skizze noch mal an.

Die Skizze1 läuft auf iOS ein ganz klein bisschen ruckeliger und nicht ganz so schön wie die Keyframes Skizze .. von jQuery ganz zu schweigen, das wäre im Vergleich dazu zäh wie Sirup.
Spielt das überhaupt noch eine Rolle?
Heutzutage hat jedes popelige Handy mehr Rechenpower als noch ein Großrechner von vor ein paar Jahren - muss man da echt auf Ressourcen schauen? Ich denke ja!

Auf mobilen Websiten spielt vor allem das look & feel eine Rolle. Je sauberer, schneller und klarer eine Site auf dem mobilen Gerät läuft, desto eher wird euer Besucher auf eurer Site bleiben - Ruckelorgien finden die meisten nicht so prickelnd.

Anmerkungen Fragen

Wenn's Fragen, Anmerkungen oder ein Fehlerreport gibt, immer her damit. Kann sein, dass ich nicht so super schnell reagieren kann wie ich eigentlich will - dazu werden's hier einfach zu viele Kommentare - aber nach wie vor bekommt hier jede Fragesteller eine Antwort!