What is HTML?



What is HTML?

1 1


better_barchart

An intro to D3.js by way of making a better bar chart!

On Github brandomr / better_barchart

Let's make a better bar chart!

This walk through covers how to go from zero to interactive bar chart as quickly as possible. Along the way we'll learn a bit about HTML5, CSS, and javascript.

Where we're going

Our goal is to end up with something that looks like this.
Hover over a country to see the percent of its population with favorable views of Shangri La
BahrainBangladeshBruneiBermudaBarbadosBoliviaBrandon020406080100Percent with favorable view

What is HTML?

HyperText Markup Language (HTML) is the standard markup language used to create webpages. An HTML document is a plain text document with an .html extension that consists of elements enclosed in <>. Here is what a very standard site might look like:

<div>
	<p>Hello world!</p>
</div>
Hello world!

What's a div?

One of the most basic building blocks of HTML pages is the div. A div is essentially a rectangular block. Here's an example of one:

<div style="display: inline-block;
				   width: 50px;
				   height: 200px;
				   background-color: #3690c0;"></div>

Let's make a basic bar chart

With just a set of div elements, we can actually make a bar chart appear on a page.

<div class="example_bar" style="height: 20px"></div>
<div class="example_bar" style="height: 50px"></div>
<div class="example_bar" style="height: 75px"></div>
<div class="example_bar" style="height: 100px"></div>
<div class="example_bar" style="height: 150px"></div>

Styling with CSS

Cascading Style Sheets (CSS) enable you to apply styles to elements of the same type or of the same class or ID. They are intended to enable the separation of document content from document presentation. Here's how I applied styling to all the bars in the bar chart at once.

<style>
.example_bar {
	  display: inline-block;
	  width: 50px;
	  background-color: #3690c0;
	  }
</style>

HTML5 and the SVG

With the advent of HTML5 came the Scalable Vector Graphic (SVG). The SVG is a canvas for creating graphics like lines/paths, rectangles, and circles. D3.js relies on the SVG to create graphics that can scale to the size of our data. Here's an example.

<svg width="100" height="100">
  <circle cx="50" cy="50" r="40" stroke="grey" stroke-width="4" fill="steelblue"></circle>
</svg>

One last thing about HTML

HTML documents are stored in local memory on a client's computer and are interpreted by the web browser. The interpretation of the HTML is rendered as the

Document Object Model (DOM)

The DOM need not be static! In fact, the beauty of D3.js is that it allows us to easily use data to manipulate the DOM.

What is D3.js?

Data-Driven Documents (D3) is a javascript library designed for the creation of web-based charting. A web page is just an HTML document and D3 let's us drive the HTML using data.

What is D3.js?

D3.js was created in 2011 by Mike Bostock, Vadim Ogievetsky, and Jeffrey Heer while they were part of Stanford's Vis Group. D3.js is open source and has become the standard for interactive visualizations for the web.

D3 is used by the New York Times (who now employs Mike Bostock) and many others. It has been leveraged for building many of the other commonly used web charting libraries.

Never start from scratch!

I never do. And nor should you. Leverage the many examples available at D3js.org and elsewhere.

D3 is Javascript

Javascript is considered the language of the web. It has been around for a while (it was first developed at Netscape in the 90's) and is now used on essentially any web page you might visit. For example, this presentation was made with Reveal.js. To load some javascript include a reference to the script file in your HTML document:

<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>

Keep in mind that like a HTML, a javascript document is simply a plain text document written in javascript with a .js extension.

Back to the bar chart

here is the data the we will use to build the chart

country favorability description Bahrain 27 Our favorability with Bahrain is at an all time low Bangladesh 35 We are doing better and better with Bangladesh Brunei 48 Could be better with Brunei all things considered Bermuda 56 Bermuda is such a nice place. A really nice place Barbados 69 Great island and they less than three us Bolivia 74 Surprising right? Who would have thought it? Brandon 87 He is our biggest fan thats for sure

Let's call the file data.csv

A .csv file is just a comma separated text file. Recognize a theme here? We will be using a lot of plain text files.

You have probably opened a .csv or two using Excel or another spreadsheet software.

Bet you didn't notice the difference.

Let's get started.

First, you'll need to create a blank web page and load D3.

<!DOCTYPE html>
<meta charset="utf-8">
    <body>
        <div>Behold, a bar chart!</div>
        <div class="barchart"></div>
        <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
         <script type="text/javascript" src="barchart.js"></script>
    </body>
</html>

You can copy and paste that into a blank file using a plain text editor. I like Text Wrangler since it's free but you could just as easily use something as basic as Notepad or something with more functionality like Sublime Text.

Make sure you save your file with a .html extension.

Let's look at what we just did

<!DOCTYPE html>
<meta charset="utf-8">
    <body>
        <div>Behold, a bar chart!</div>
        <div class="barchart"></div>
        <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
         <script type="text/javascript" src="barchart.js"></script>
    </body>
</html>
  • Created a div which serves as a title (Behold...)
  • Created a div with the class barchart which will be a container for our chart
  • Sourced d3.v3.min.js from the D3js.org. Alternatively, we could save the d3.js file locally and source it from our file directory
  • Sourced barchart.js, the file that we are going to create and which will define our chart

Onto the D3

Open up another blank plain text file and save it in the same directory as your .html file. Call it barchart.js

We'll use this file to write out our D3 script.

Setting up our canvas

First, let's define the margins of our chart.

var margin = {top: 50, right: 20, bottom: 30, left: 60}
var width = 660 - margin.left - margin.right
var height = 400 - margin.top - margin.bottom;

Next, let's scale our x and y axes to fit the canvas:

var x = d3.scale.ordinal()
 .rangeRoundBands([0, width], .1);

var y = d3.scale.linear()
 .range([height, 0]);

Adding the SVG

Let's append a SVG element to our <div class="barchart"> container:

var svg = d3.select(".barchart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Remember, the SVG is a container for our chart. Specifically, we append a SVG and then append to that a G element which lets us group the building blocks together.

Reading in our data

First, define a function called type that ensures favorability, a continuous variable, is read in as a number instead of a string.

function type(d) {
  d.favorability = +d.favorability;
  return d; 
  }

Now we can read in our data, passing the name of the data file, the function type, and asking the browser to log the data to the console.

d3.csv("data.csv", type, function(error, data) {
  console.log(data);

A recap

Your javascript file should now look like:

var margin = {top: 50, right: 20, bottom: 30, left: 60}
var width = 660 - margin.left - margin.right
var height = 400 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
 .rangeRoundBands([0, width], .1);

var y = d3.scale.linear()
 .range([height, 0]);

var svg = d3.select(".barchart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

function type(d) {
  d.favorability = +d.favorability;
  return d;
}

d3.csv("data.csv", type, function(error, data) {
  console.log(data);

Setting the x and y domains

Now that we have read in our data, we map our data to the x and y domain. We use the variables x and y which we defined earlier and we call them using the x and y ranges within our data set.

x.domain(data.map(function(d) { return d.country; }));

y.domain([0, 100]);

This ensures that when we use our x or y variable on an x or y data point, it scales to the appropriate number for our chart.

I set the y domain (favorability) as between 0 and 100 since we know this is a percent out of 100. The x domain is mapped to the number of unique data points within the country variable.

Next, let's build the bars

Add the following code block to barchart.js

svg.selectAll(".bar")
    .data(data)
    .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) { return x(d.country); })
      .attr("width", x.rangeBand())
      .attr("y", function(d) { return y(d.favorability); })
      .attr("height", function(d) { return height - y(d.favorability); })
We just selected the SVG variable and bound the data to the SVG while appending a new rect for each row in data.csv.

We set the x and y position using the scales we defined earlier and set the width and the height using our x scale and the the favorability variable in the dataset.

We now have something that looks like
Hmm...

Coloring our bar chart

With a bit of styling back in our HTML document, we can style the color of the bar chart.

body {
    background-color: black;
    font: 10px sans-serif;
    margin-left: 10%;
}

.bar {
    fill: #3690c0;
    }

.bar:hover {
    fill: #fc4e2a;
    }

We just set the color of the bar by styling anything where class="bar". We also added a second color for when someone hovers over the bar.

We now have something that looks like
...better, but we still need our axes!

Defining our axes

Now, we need to define the axes

var xAxis = d3.svg.axis()
 .scale(x)
 .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

We can call d3.svg.axis() to succinctly create axes. We then scale them using our x and y variables.

Plot the axes

Now we can add our x and y axes to the SVG canvas:

svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);<p></p>

svg.append("g") .attr("class", "y axis") .call(yAxis)

Append a new g element to the variable svg and call the xAxis or yAxis variable to appropriately scale and locate the axes.

We now have something that looks like
BahrainBangladeshBruneiBermudaBarbadosBoliviaBrandon0102030405060708090100
Now we're cooking!

Styling the axes

Let's add some styling to the axes back in the HTML document.

.axis {
    font: 12px sans-serif;
    fill: #fff;
}

.axis path,
.axis line {
    fill: none;
    stroke: #969696;
    shape-rendering: crispEdges;
}

.x.axis path {
    display: none;
}

Note that we changed the .x.axis path to display: none which turns off the x axis line.

Updating the tick marks

Let's add to our D3 code to improve the tick marks and labels.

var xAxis = d3.svg.axis()
    .scale(x)
 .orient("bottom")
 .tickSize(0)
    .tickPadding(5);

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(4)
    .tickSize(3);

We remove the ticks completely from the x axis and add padding between the bars and the country labels. Also, we limit the number of labels (between the min/max) on the y axis to 4 and we shrink the ticks.

We now have something that looks like
BahrainBangladeshBruneiBermudaBarbadosBoliviaBrandon020406080100
Not too shabby!

Let's recap

Our javascript file barchart.js should look like

var margin = {top: 50, right: 20, bottom: 30, left: 60};
var width = 660 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
 .rangeRoundBands([0, width], .1);

var y = d3.scale.linear()
 .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickSize(0)
    .tickPadding(5);

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(4)
    .tickSize(3);

var svg = d3.select(".barchart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

function type(d) {
  d.favorability = +d.favorability;
  return d;
}

d3.csv("data.csv", type, function(error, data) {
  console.log(data);

  x.domain(data.map(function(d) { return d.country; }));
  y.domain([0, 100]);

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis);

  svg.selectAll(".bar")
      .data(data)
    .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) { return x(d.country); })
      .attr("width", x.rangeBand())
      .attr("y", function(d) { return y(d.favorability); })
      .attr("height", function(d) { return height - y(d.favorability); })
});

Let's recap

Our HTML file should look like
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
	background-color: black;
	font: 10px sans-serif;
	margin-left: 10%;
}

.bar {
    fill: #3690c0;
}

.bar:hover {
    fill: #fc4e2a;
}
    
.axis {
    font: 12px sans-serif;
    fill: #fff;
}

.axis path,
.axis line {
    fill: none;
    stroke: #969696;
    shape-rendering: crispEdges;
}

.x.axis path {
    display: none;
}
</style>
    <body>
        <div>Behold, a bar chart!</div>
        <div class="barchart_step1"></div>
        <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
         <script type="text/javascript" src="js/barchart_step1.js"></script>
    </body>
</html>

Adding a tooltip

Let's add a tooltip when we hover over the bar. This will reveal the favorability data point underlying the bar.

First, let's define a variable called tooltip

var tooltip = svg.append("text")
    .style("position", "absolute")
    .style("z-index", "1000")
    .style("visibility", "hidden")
    .style("fill", "#ffffff");

Let's have our tooltip begin as hidden.

Now we add mouse events

Let's look back at the code that executes the bars themselves.

svg.selectAll(".bar")
    .data(data)
    .enter().append("rect")
Right after
.attr("height", function(d) { return height - y(d.favorability); })
we will add a mouseover event:
.on("mouseover", function(d){ 
     tooltip.html(d.favorability )
     .style("visibility", "visible")
     .attr("x", function() { return x(d.country) + x.rangeBand()/2 - 10; })
     .attr("y", function() { return y(d.favorability) + 25; })
     ;})

Now we add mouse events

Let's also add a mouseout event right after that:

.on("mouseout", function(d){ 
     tooltip.style("visibility", "hidden")
     ;});

We now have something that looks like
BahrainBangladeshBruneiBermudaBarbadosBoliviaBrandon020406080100
Cool!

Finishing up: the mouseover detail

Now let's add the variable for the notes on mousover:

var notes = d3.select(".barchart").append("div")
    .attr("class", "notes")
    .html("Hover over a country to see the percent of its population with"
        + "<span style="color:white"> favorable views of Shangri La</span>")
    .style("margin-bottom", "-50px")
    .style("margin-top", "20px")
    .style("margin-left", "10%")
    .style("font-size", "16px")
    .style("color", "#fc4e2a")
    .style("width", "400px")
    .style("line-height", "1em")
    .style("text-align", "left");

Note that when we define this variable we can define the html to show on load (Hover over a country to see...)

Update our mouseover

Find the mouseover. Right after

.attr("y", function() { return y(d.favorability) + 25; })
we need to add
d3.select(".notes")
        .html(d.country + ": <span style="color:white">" + 
              d.favorability + "% favorability. </span><br>" + d.description )

This indicates that we need to grab the div with class="notes" on mouseover and change the text to the variables of our choice.

We now have something that looks like
Hover over a country to see the percent of its population with favorable views of Shangri La
BahrainBangladeshBruneiBermudaBarbadosBoliviaBrandon020406080100Percent with favorable view
Bingo!

Let's recap

Our javascript file barchart.js should look like

var margin = {top: 50, right: 20, bottom: 30, left: 60};
var width = 660 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
 .rangeRoundBands([0, width], .1);

var y = d3.scale.linear()
 .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickSize(0)
    .tickPadding(5);

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(4)
    .tickSize(3);

var notes = d3.select(".barchart").append("div")
    .attr("class", "notes")
    .html("Hover over a country to see the percent of its population with"
              + "<span style="color:white"> favorable views of Shangri La</span>")
    .style("margin-bottom", "-50px")
    .style("margin-top", "20px")
    .style("margin-left", "10%")
    .style("font-size", "16px")
    .style("color", "#fc4e2a")
    .style("width", "400px")
    .style("line-height", "1em")
    .style("text-align", "left");

var svg = d3.select(".barchart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    function type(d) {
  d.favorability = +d.favorability;
  return d;
}

d3.csv("data.csv", type, function(error, data) {
  console.log(data);

  x.domain(data.map(function(d) { return d.country; }));
  y.domain([0, 100]);

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", -45)
      .attr("dy", ".21em")
      .style("text-anchor", "end")
      .text("Percent with favorable view");

  svg.selectAll(".bar")
      .data(data)
    .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) { return x(d.country); })
      .attr("width", x.rangeBand())
      .attr("y", function(d) { return y(d.favorability); })
      .attr("height", function(d) { return height - y(d.favorability); })
      .on("mouseover", function(d){ 
              tooltip.html(d.favorability )
              .style("visibility", "visible")
              .attr("x", function() { return x(d.country) + x.rangeBand()/2 - 10; })
              .attr("y", function() { return y(d.favorability) + 25; })

              d3.select(".notes")
              .html(d.country + ": <span style="color:white">" + 
              d.favorability + "% favorability. </span><br>" + d.description )
              ;})

      .on("mouseout", function(d){ 
              tooltip.style("visibility", "hidden")
              ;});

var tooltip = svg.append("text")
    .style("position", "absolute")
    .style("z-index", "1000")
    .style("visibility", "hidden")
    .style("fill", "#ffffff");
});

Let's recap

Our HTML file should look like
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
	background-color: black;
	font: 10px sans-serif;
	margin-left: 10%;
}

.bar {
    fill: #3690c0;
}

.bar:hover {
    fill: #fc4e2a;
}
    
.axis {
    font: 12px sans-serif;
    fill: #fff;
}

.axis path,
.axis line {
    fill: none;
    stroke: #969696;
    shape-rendering: crispEdges;
}

.x.axis path {
    display: none;
}
</style>
    <body>
        <div>Behold, a bar chart!</div>
        <div class="barchart_step1"></div>
        <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
         <script type="text/javascript" src="js/barchart_step1.js"></script>
    </body>
</html>