Kolumne: Dino talks

Web-Experience mit Bootstrap stylen
Kommentare

Twitter Bootstrap wird überall im Web verwendet, ob über standardmäßige oder benutzerdefinierte CSS-Styles. Die Klassennamen sowie die neuen HTML-Metaelemente, die Bootstrap definiert, sind inzwischen in den meisten Sites gebräuchlich und arbeiten Seite an Seite mit nativen HTML-Elementen wie zum Beispiel Kontrollkästchen, Optionsfeldern und Dropdown-Listen. Allerdings harmoniert eine gute alte browserspezifische Dropdown-Liste nicht mit den farbenfrohen nativen Komponenten von Bootstrap. Dieser Artikel zeigt, wie Sie mithilfe von eigenen Bootstrap-spezifischen Dropdownlisten die Web-Experience vereinheitlichen.

Während sich eine Dropdown-Liste mithilfe von CSS-Einstellungen problemlos formatieren lässt, wird sie immer ein wenig unpassend aussehen, wenn sie im Kontext einer Bootstrap-Benutzeroberfläche verwendet wird. Das typische Erscheinungsbild einer Dropdown-Liste hängt stark vom Browser ab und erscheint leicht unterschiedlich für Benutzer, die sich über verschiedene Browser mit der Website verbinden. Unterschiede in der Art und Weise, wie Browser grundlegende HTML-Elemente rendern, sind nie ein großes Problem für Entwickler gewesen – als hätte es immer so sein müssen. Sollten wir uns jetzt beschweren?

Das Eigenartige ist, dass Bootstrap die Schaltflächen und Eingabetextfelder neu schreibt, andere grundlegende Elemente wie zum Beispiel Kontrollkästchen, Optionsfelder und Dropdown-Listen aber gar nicht anfasst. Im Endeffekt erhält man ein allumfassendes Gefühl von Inkonsistenz. Allerdings ist das Erzeugen einer Bootstrap-spezifischen Dropdown-Listenkomponente nicht allzu schwierig. Man braucht lediglich ein Ad-hoc-Markup-Template und etwas JavaScript. Machen wir nun den ersten Schritt.

Bootstrap-Dropdown-Menüs erweitern

Selbst wenn Bootstrap keine ausgewachsene Dropdown-Komponente bereitstellt – eine, die aus SELECT- und OPTION-Elementen resultiert – sind die vorhandenen Dropdown-Menüelemente schon eine recht gute Annäherung. Das Codefragment in Listing 1 definiert ein Dropdown-Menü in Bootstrap.

Listing 1
<div class="btn-group">
  <a class="btn btn-default dropdown-toggle"
      data-toggle="dropdown>
      Get one <span class="caret"></span></div>
  </a>
  <ul class="dropdown-menu">
     @foreach (var item in list)
     {
         <li><a href="#"><span>@item.Text</span></a></li>
     }
  </ul>
</div>

Die Schaltfläche zeigt anfangs den Text „Get one“ gefolgt von einem Caret-Symbol an. Wenn der Benutzer auf die Schaltfläche klickt, erscheint ein Menü, das mit allen Listenelementen gefüllt ist. In Bootstrap existieren Dropdown-Menüs nur, um eine Liste von Hyperlinks für die Navigation anzubieten. Wenn Sie jedoch ein wenig JavaScript mit dem obigen Markup zusammenpacken, können Sie Klicks auf die Hyperlinks verarbeiten und die Navigation für Änderungen der Benutzeroberfläche überschreiben. Wird ein Bootstrap-Dropdown-Menü in ein klassisches Dropdown-Menü überführt, ist darauf zu achten, dass auf den Listenelementen keine Hyperlinks definiert sind. Im Idealfall setzen Sie im href-Attribut einen Anker auf # innerhalb der LI-Elemente. Wenn Sie es leer lassen, kann das Element noch immer angeklickt werden, doch der Mauszeiger ändert sich so, als würde es sich um einfachen Text handeln.

Vom Dropdown-Menü zur Dropdown-Liste

Das folgende kleine JavaScript-Codefragment macht es möglich, ein Dropdown-Menü in eine Dropdown-Listenkomponente zu überführen:

$(".dropdown-menu li a").click(function () {
  var selected = $(this).text();
  $(this).parents('.btn-group')
    .find('.dropdown-toggle')
    .text(selected + '');
});

Der Code verbindet einen gegebenen Handler mit dem Klickereignis eines A-Elements von untergeordneten LI-Elementen, die unter einem als Dropdown-Menü formatierten Element zu finden sind. Wenn Sie den jQuery-Selektor im obigen Codefragment auf die HTML-Struktur eines Bootstrap-Dropdown-Menüs abbilden, wird der Handler aktiviert, wenn ein Ankerelement aus dem Dropdown-Menü angeklickt wird. Das Skript holt sich den Text des angeklickten Hyperlinkelements und überschreibt ihn innerhalb der Schaltflächenbeschriftung. Abbildung 1 vermittelt eine Vorstellung von den Abläufen.

Abb. 1: Schaltflächenbeschriftung ändern, wenn Dropdown-Element ausgewählt wird

Abb. 1: Schaltflächenbeschriftung ändern, wenn Dropdown-Element ausgewählt wird

Die Idee ist recht einfach. Wenn der Benutzer auf ein Listenelement klickt, startet ein Skript und ändert die Schaltflächenbeschriftung, um das angeklickte Element zu kennzeichnen. Letztlich handelt es sich um den gleichen Effekt wie bei einem Dropdown-Element. Sind wir dann schon fertig? Nein, denn das ist nur der erste Schritt, wenn auch in die richtige Richtung.

Anstehende Probleme

Wenn Sie eine SELECT-Komponente verwenden, verfügt jedes OPTION-Element sowohl über einen Anzeigetext als auch einen Wert. Der Text ist als Klartext für den Benutzer gedacht, während der Wert für das Backend-System sinnvoll sein muss. Wo würden Sie im obigen Codefragment eine Wert-ID unterbringen? Dies lässt sich beispielsweise mit einem zusätzlichen data-value-Attribut für das A-Element an der Wurzel der Markup-Hierarchie bewerkstelligen:

<div class="btn-group">
  <a class="btn btn-default dropdown-toggle"  
    data-value="..."
    data-toggle="dropdown>
    Get one <span class="caret"></span></div>
  </a>
  <ul> ... </ul>
</div>

Die Lösung arbeitet wunderbar, außer, dass sie nutzlos in allen Szenarios ist, in denen ein HTML-Formular zu posten ist. Wenn Sie ein HTML-Formular posten, sammelt der Browser Werte von allen Eingabefeldern innerhalb der Grenzen des HTML-Formulars. Das normale HTML-SELECT-Element gilt als Eingabeelement. Browser fragen es nach dem momentan ausgewählten Element ab, bevor die Daten gepostet werden. Dies wird nicht passieren, wenn das SELECT-Element durch eine Bootstrap-Schaltflächengruppe ersetzt wird.

Um die eindeutige ID einer SELECT OPTION zu verarbeiten, ist es besser, ein verstecktes Eingabefeld hinzuzufügen, das mit einer eindeutigen ID markiert ist. Es erübrigt sich zu sagen, dass diese ID auch die Dropdown-Liste als Ganzes identifiziert. Das Markup für die Dropdownliste ändert sich dadurch etwas:

<div class="btn-group">
  <a class="btn btn-default dropdown-toggle"
    data-toggle="dropdown>
      <input type="hidden" name="Id" id="Id" value="..." />
    Get one <span class="caret"></span></div>
  </a>
  <ul> ... </ul>
</div>

Das versteckte Feld speichert die ID des momentan ausgewählten Elements. Dennoch muss jedes aufgelistete Element durch seine eigene ID gekennzeichnet sein. Dazu können Sie beispielsweise ein benutzerdefiniertes data-value-Attribut zum Hyperlink hinzufügen, der eine mögliche Auswahl darstellt:

<ul> 
  <li>
    <a href="#" data-value="Value"> Text </a>
  </li>
</ul>

Der Code, der auf das Klicken des Benutzers reagiert, muss etwas angepasst werden, um die Änderung widerzuspiegeln:

$(".dropdown-menu li a").click(function () {
  var selected = $(this).text();
  $(this).parents('.btn-group')
    .find('.dropdown-toggle')
    .text(selected + '');
  var dataValue = $(this).attr("data-value");
  $(this).parents('.btn-group')
    .find('input[type=hidden]')
    .val(dataValue);
});

Nachdem wir über eine funktionsfähige Minimallösung verfügen, können wir uns der nächsten Herausforderung stellen, nämlich wie man die Dropdown-Komponente in praktischen HTML-Seiten wirksam einsetzt.

Die neue Komponente in realen Seiten verwenden

Webseiten und Weblösungen erstellt man heute hauptsächlich nach zwei Verfahren. Bei dem einen drückt man das Markup jeder Seite über reines HTML aus. Wenn Sie also eine Dropdown-Liste benötigen, erstellen Sie manuell das HTML-Template und binden es an irgendwelche Inhalte, die Sie vom verfügbaren View-Modell erhalten. Entspricht das Ihrem Arbeitsstil, brauchen Sie lediglich eine passende jQuery-Methode, um den aktuell ausgewählten Wert abzufragen. Es könnte nicht einfacher sein:

var selected = $("#drop-down-id").val();

Mit dem vorher erwähnten Markup entspricht die ID der gesamten Komponente der ID des internen versteckten Felds. JavaScript-Hilfsroutinen stellen sicher, dass das versteckte Feld beständig mit der ID des ausgewählten Elements gefüllt wird. Etwas komplizierter sieht die Sache aus, wenn Sie vorrangig aus ASP.NET-MVC-Razor-Ansichten heraus arbeiten.

Razor erlaubt es, wiederverwendbare Teile des Markups in HTML-Hilfsroutinen zu kapseln. Die meisten Razor-Hilfsroutinen sind in Code als Erweiterungsmethoden für die Klasse HtmlHelper definiert. Derartige Komponenten bauen ihr HTML-Markup mithilfe der nativen TagBuilder-Klasse oder durch einfache Zeichenfolgenverkettung auf. Allerdings können Sie in ASP.NET MVC auch Markup-basierte wiederverwendbare Komponenten erstellen. Diese Option scheint bestens geeignet, um Bootstrap-Erweiterungen zu kreieren. Wir legen dazu eine neue Datei im Projekt App_Code an und nennen sie BootstrapExtensions.cshtml. Beachten Sie, dass sich zwar der Dateiname beliebig wählen lässt, der Ordnername App_Code jedoch nicht. Listing 2 zeigt den vollständigen Quellcode.

Listing 2
@using System.Web.Mvc
@helper DropDown(string id, 
    string title, 
    SelectList list, 
    string buttonStyle="btn-default", 
    string width="auto", 
    string caret="caret")
{
  var selectedText = title;
  var selectedValue = (string) list.SelectedValue ?? "";
  if (!String.IsNullOrWhiteSpace(selectedValue))
  {
    selectedText = (from item in list 
                    where item.Value == selectedValue 
                    select item.Text).FirstOrDefault(); 
  }
  <div class="btn-group" style="width:@width">
    <a class="btn @buttonStyle dropdown-toggle" 
       data-toggle="dropdown" 
       style="width:100%">
        <input type="hidden" name="@id" id="@id" value="@selectedValue" />
        <div class="bext-dropdown-selected">@selectedText</div>
        <div style="float:right"><span class="@caret"></span></div>
        <div class="clearfix"></div>
    </a>
    <ul class="dropdown-menu" style="width:100%">
        @foreach (var item in list)
        {
            <li>
               <a href="#" data-value="@item.Value">@item.Text</a>
            </li>
        }
    </ul>
  </div>
}

Nun verfügen Sie über eine neue Komponente namens DropDown, die sich in Razor-Ansichten einsetzen lässt. Der Konstruktor übernimmt einige Parameter, wie zum Beispiel die ID der Dropdown-Komponente, den anzuzeigenden Text, wenn kein Element ausgewählt ist, die Liste der anzuzeigenden Elemente und einige Stileigenschaften. In Ihren Razor-Dateien können Sie Code wie etwa den folgenden verwenden:

@BootstrapExtensions.DropDown(
  "btn-select", 
  "Get one", 
  new SelectList(Model.Demos, "Id", "Name"), "btn-primary btn-lg", "200px")

Offensichtlich unterscheidet sich das kaum von der Verwendung einer klassischen HTML-Hilfskomponente wie Html.BeginForm oder Html.CheckBox. Das in Ihrer Seite ausgegebene Markup entspricht dem weiter oben gezeigten, das eine Dropdown-Liste erzeugt und mit etwas JavaScript umgibt. Um ein Projekt zu konfigurieren, müssen Sie in den Ordner App_Code die Datei BootstrapExtensions.cshtml aufnehmen und auf eine JavaScript-Datei mit dem im Artikel gezeigten Codefragment verweisen. Sie können auch eine CSS-Datei referenzieren, wenn Sie das Markup-Template weiter anpassen möchten. Zu beachten ist, dass das Dropdownmenü die in Bootstrap definierten Farben und Einstellungen verwendet. Wenn Sie auf CSS zurückgreifen, können Sie die Hervorhebungs- oder Textfarben nach Belieben ändern. In einer benutzerdefinierten CSS-Datei sollten Sie insbesondere die folgenden Klassen überschreiben:

.dropdown-menu li > a:hover,
.dropdown-menu li > a:focus,
.dropdown-submenu:hover > a {
  background-image: none;
  background-color: #444;
  color: #cc0;
}

Mit dem nachfolgenden Skript können Sie per Programm den Wert des Elements in der von Bootstrap abgeleiteten Dropdownliste lesen:

$("#selValue").text($("#btn-select").val());

Normalerweise ist es nicht erforderlich, von einer Dropdown-Liste den Text des ausgewählten Elements zu lesen. Sollten Sie aber genau dies brauchen, können Sie das bei der gegebenen Struktur des Markups wie folgt erreichen:

$("#selText").text($("#btn-select").next().text());

Der Selektor next() geht davon aus, dass das versteckte Feld in der Hierarchie unmittelbar vor dem DIV-Element kommt, das den angezeigten Text enthält. Wenn Sie das HTML-Template anpassen, müssen Sie den JavaScript-Code ebenfalls aktualisieren.

Zusammenfassung

Es gibt viele andere Aspekte einer Dropdownliste, die sich verbessern lassen, gerade wenn Dropdown-Listen aus Razor heraus verwendet werden. Insbesondere könnten Sie den Umgang mit Optionsgruppen verbessern und Unterstützung für Trennelemente und Symbole hinzufügen. Wie das geht, erfahren Sie in den nächsten Artikeln. Es wird dann auch gezeigt, wie Sie zu Bootstrap-freundlichen Kontrollkästchen und Optionsfeldern kommen. Den in diesem Artikel besprochenen Code können Sie live testen und herunterladen.

Aufmacherbild: Leather men’s shoes von Shutterstock / Urheberrecht: Devin_Pavel

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -