Daten mit D3 visualisieren (Teil 1)

D3 (Data-Driven Documents) ist eine JavaScript-Bibliothek zum Verknüpfen von Daten mit den DOM von HTML. Meist resultiert daraus eine Diagramm, oder eine Karte. D3 lässt sich aber auch für einfachere Visualisierungen, wie z. B. eine Liste einsetzen. In einem Beispiel zeige ich, wie man aus den Daten in einer JSON-Datei eine mit Bootstrap formatierte Liste erstellt.

Motivation

Auf http://m-entrup.de/cv/ ist das Ergebnis meiner Visualisierung zu sehen. Die visualisierten Daten sind in der Datei cv.json zu finden. Es geht somit darum, einen strukturierten Datensatz in ein komplexes Layout einzubetten. In diesem ersten Teil beschränke ich mich auf ein statisches Layout, welches in weiteren Teilen um dynamische Funktionen, wie eine Suchfunktion, ergänzt werden soll.

Was ist D3.js?

D3.js is a JavaScript library for manipulating documents based on data.

D3 verknüpft Daten (z. B. aus externen Quellen) mit dem DOM der Website und manipuliert damit die Website. In diesem ersten Teil möchte ich mich nur auf das Hinzufügen von neuen Elementen beschränken. Im zweiten Teil soll es dann um die Manipulation von schon bestehenden Elementen gehen.

Laden der Daten

  d3.json("/data/cv.json", function (data){
    var firstName =  data[Software][0].name;
    ...
  });

Mit dieser einen Funktion laden wir die JSON-Datei, wodurch diese in der anonymen Funktion als das Objekt data zur Verfügung steht. Über den Befehl data[Software][0].name greife ich auf Das erste Element im Array Software zu und selektiere dessen Eigenschaft name.

Verarbeiten der Daten

Um die aus der JSON-Datei geladenen Daten effektiv zu verarbeiten, benötige ich zwei Schleifen:

  for (var cat in data){
    d3.select("#d3-include").append("div")
      .attr("class", "row")
      .append("div")
      .attr("class", "col-xs-12")
      .append("h2")
      .text(cat);
    for (var i = 0; i < data[cat].length; i++) {
      ...
    }
  }

Die erste Schleife wählt die benannten Array-Elemente Software, Programmiersprachen und Sonstiges aus. Daran erkennt man den Vorteil der Implementierung der for-Schleife in JavaScript, bei der Indizes der Elemente ausgewählt und nicht die Elemente selbst. Ich verwende nämlich die Indizes um innerhalb der äußeren Schleife die Überschriften damit zu erzeugen. Dabei verwende ich das Verketten (Chaining) von Funktionen, das auch bei jQuery genutzt wird. Viele Funktionen geben das Object zurück, auf dem sie aufgerufen wurden. In diesem Fall verschachtele ich zwei div-Elemente, um das Grid-System von Bootstrap einzuhalten.

for (var i = 0; i < data[cat].length; i++) {
  var date = data[cat][i];
  var newDiv = d3.select("#d3-include").append("div")
    .attr("class", "row")
    .append("div")
    .attr("class", "col-xs-12");
  newDiv.append("h3")
    .text(date.name)
  newDiv.append("p")
    .html(function() {
    var content = ""
    var j = 0;
    while (j < date.value) {
      content += '<span class="glyphicon glyphicon-star" aria-hidden="true"></span>';
      j++;
    }
    while (j < 5) {
      content += '<span class="glyphicon glyphicon-star-empty" aria-hidden="true"></span>';
      j++;
    }
    newDiv.append("p")
      .attr("class", "text-justify")
      .html(date.description);
    return content;
  });
}

Die innere Schleife erzeugt für jeden Datensatz ein div, welches mit den Werten der Attribute name, value und description befüllt wird. Erneut sind wegen Bootstrap zwei div-Elemente verschachtelt. date.name wird mit der Funktion text() zu einem h3-Element hinzugefügt, da es sich bei date.name um einen einfachen String ohne Markup handelt. Bei date.description verwende ich im String HTML-Tags, um Links zu erzeugen. Aus diesem Grund muss statt der Funktion text() die Funktion html() verwendet werden, um den String in ein p-Element einzufügen. Verwendet man text(), dann werden Sondderzeichen so ersetzt, dass sie in HTML darstellbar sind (z. B. &lt; für <).

Mit dem Attribut value erzeuge ich unterschiedliche Glyphicons, die ein Bewertungssystem mit 5 Sternen nachahmen, wie es im Onlinehandel üblich ist. Der Funktion html() übergebe ich dabei nicht direkt einen String, sondern eine anonyme Funktion erzeugt einen String und gibt diesen als Rückgabewert an html() weiter.

Insgesamt sind nur 36 Zeilen JavaScript-Code notwendig, damit der JSON-Datensatz in das HTML-Dokument eingebettet wird. Wie umfangreich der Datensatz ausfällt, ist dabei unerheblich.

Ausblick auf Teil 2

Die bisherige Vorgehensweise hat keinen Vorteil gegenüber einem Script (z. B. in PHP geschrieben), welches die Liste auf dem Server erzeugt und als reines HTML-Dokument an den Browser ausliefert. Ich möchte deshalb noch eine Suchfunktion hinzufügen, die die Attribute name und description nach dem eingegebenen Begriff durchsucht und die Liste auf Datensätze beschränkt, die einen Treffer enthalten. Dazu sind die beiden D3-Funktionen enter() und exit() notwendig. Die Suche wird dabei im Browser ausgeführt und es ist keine weitere Kommunikation mit dem Server notwendig.

Ausblick auf Teil 3

Im dritten Teil möchte ich mehrere Diagramme mit D3 erstellen. Mein Datensatz enthält mit value nur einen Zahlenwert als Attribut, da reicht aber um die Grundlagen zur Erstellung von dynamischen Diagrammen zu zeigen.