Chapter 3: Integrating Charts on a Page

You might expect a data visualization for the web to be featured very prominently on the page, or even make up the entire web page. That’s not always the right approach, though. The best visualizations are effective because they help the user understand the data, not because they “look pretty” on the page. Some data may be straightforward enough to present without context, but meaningful data probably isn’t. And if our presentation requires context, its visualizations are likely sharing the page with other content. When we design web pages, we should take care to balance any individual component with the page as a whole. If a single visualization is not the entire story, it shouldn’t take up all (or even most) of the space on the page. It can be challenging, however, to minimize the space a traditional chart requires. There are, after all, axes, labels, titles, legends, and more to place.

Edward Tufte considered this problem in his groundbreaking work The Visual Display of Quantitative Information, and he proposed a novel solution he called sparklines. Sparklines are charts stripped to their bare essentials, presented without the aforementioned elements we often see in a chart. Sparklines can present a lot of information in very little space, even to the point where it is possible to include a chart right in the middle of a sentence. There is no need for “See figure below” or “Click for larger view.” One of Tufte’s earliest examples presents the glucose level of a medical patient; figure shows a reproduction.

170,134,115,128,168,166,122,81,56,39,97,114,114,130,151,184,148,145,134,145,145,145,143,148,224,181,112,111,129,151,131,131,131,114,112,112,112,124,187,202,200,203,237,263,221,197,184,185,203,290,330,330,226,113,148,169,148,78,96,96,96,77,59,22,22,70,110,128  128  Glucose

Tufte’s classic sparkline example shows a lot of information in a small space.

In a mere 154×20 pixels we’ve shown the patient’s current glucose level, its trend for more than two months, high and low values, and the range of normal values. This high information density makes sparklines effective any time space is a premium—inline in textual paragraphs, as cells in tables, or as part of information dashboards.

Sparklines do have disadvantages of course. They cannot provide as much fine-grained detail as a full-size chart with axes and labels. They also cannot support significant interactivity, so we can’t give users a lot of flexibility in selecting data or zooming in for detail. But for many visualizations, these aren’t major concerns. Plus, as we’ll see in this chapter’s examples, the web gives us the chance to augment sparklines in ways that aren’t possible in print.

There are a few JavaScript libraries and toolkits for creating sparklines, but we’ll focus on the most popular of them: jQuery sparklines. As the name implies, this open source library is an extension to jQuery. The examples in this chapter look closely at how to use these tools to incorporate information dense visualizations into your web page. Here’s what you’ll learn:

Creating a Classic Sparkline

As later examples will demonstrate, the sparklines library is both flexible and powerful, and we can use it in many different contexts. As a start, though, it seems appropriate to use the library to create a sparkline exactly as Edward Tufte first defined it. The process is quite straightforward and only takes four simple steps.

Step 1: Include the Required JavaScript Libraries

Since we’re using the jQuery sparklines library to create the chart, we need to include that library in our web pages, along with jQuery. Both jQuery and sparklines are available on public content distribution networks (CDNs). For this example (and the others in this chapter), we’ll use the CloudFlare CDN, but for some notes on the advantages and disadvantages of using CDNs, see Chapter 2.

In addition to the jQuery library, sparklines rely on the HTML canvas feature. Since Internet Explorer didn’t support canvas until version 9, we’ll use some special markup in line 9 to ensure that IE 8 and earlier will load an additional library (excanvas.min.js), just like we did in Chapter 2. Here’s the skeleton with which we start:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <!-- Content goes here -->
    <!--[if lt IE 9]><script src="js/excanvas.min.js"></script><![endif]-->
    <script 
      src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js">
    </script>
    <script 
      src="//cdnjs.cloudflare.com/ajax/libs/jquery-sparklines/2.0.0/"+
          "jquery.sparkline.min.js">
    </script>
  </body>
</html>

As you can see, we’re including the JavaScript libraries at the end of the document. This approach lets the browser load all of the document’s HTML markup and begin laying out the page while it waits for the server to provide the JavaScript libraries.

Step 2: Create the HTML Markup for the Sparkline

Because we’re closely integrating the sparkline chart with other elements, we simply use a <span> tag to hold the HTML markup for our visualization, rather than using a <div>. In addition to the chart itself, we include the final value and a label as standard HTML. Here is the HTML for the glucose sparkline.

1
2
3
4
5
6
7
8
9
10
<p>
  <span class="sparkline">
    170,134,115,128,168,166,122,81,56,39,97,114,114,130,151,
    184,148,145,134,145,145,145,143,148,224,181,112,111,129,
    151,131,131,131,114,112,112,112,124,187,202,200,203,237,
    263,221,197,184,185,203,290,330,330,226,113,148,169,148,
    78,96,96,96,77,59,22,22,70,110,128
  </span>
  128 Glucose
</p>

Compared to other visualizations, two characteristics of our sparkline chart are unusual.

Both of these differences are optional; we could construct the chart as in other visualizations by passing data to a JavaScript function and identifying its container with a unique id. For sparklines, however, the approach we’re using here often makes more sense. By including the chart data directly in the HTML, we can easily see the data’s relation to other content on the page. It’s clear, for example, that the final value of our chart (128) is the same as the value we’re using for the label. If we had made a mistake and used a different value for the label, the error would be much easier to spot and correct. Using a common class for all sparklines instead of unique ids simplifies how we might use the library to create multiple charts on one page. With unique ids we would have to call a library function for every chart. With a common class, on the other hand, we need only call a single library function to create multiple charts. That’s especially helpful when a web page contains a lot of sparklines.

Step 3: Draw the Sparkline

Now that we’ve included the necessary libraries and set up our HTML, it’s remarkably easy to draw the charts. In fact, a single line of JavaScript is sufficient. We simply select the containing element(s) using jQuery ($('.sparkline')) and call the sparklines plugin.

1
2
3
$(function() {
    $('.sparkline').sparkline();
}

As you can see in figure , the sparklines library creates a standard sparkline from our data:

170,134,115,128,168,166,122,81,56,39,97,114,114,130,151, 184,148,145,134,145,145,145,143,148,224,181,112,111,129, 151,131,131,131,114,112,112,112,124,187,202,200,203,237, 263,221,197,184,185,203,290,330,330,226,113,148,169,148, 78,96,96,96,77,59,22,22,70,110,128 128 Glucose
The default sparkline options differ slightly from the classic example.

The library’s default options don’t quite match Tufte’s classic sparkline definition. The differences include colors, chart type, and density. We’ll tweak those next.

Step 4: Adjust the Chart Style

To make our sparkline match Tufte’s definition exactly, we can specify new values for some of the default options. Let’s collect the changes first, and then see how to pass them to the library.

To pass these options to sparklines, we construct a JavaScript object and include it as the second parameter in the sparkline function call. The function’s first parameter is the data itself, which here we specify with 'html' because our data is included in the HTML markup.

1
2
3
4
5
6
7
8
9
10
$('.sparkline').sparkline('html',{
    lineColor: "dimgray",
    fillColor: false,
    defaultPixelsPerValue: 1,
    spotColor: "red",
    minSpotColor: "red",
    maxSpotColor: "red",
    normalRangeMin: 82,
    normalRangeMax: 180,
});

To complete our transformation to Tufte’s original we can style the HTML content as well. Making the final value the same color as the key data points clarifies that connection, and making the chart label bold emphasizes it as a title.

1
2
3
4
5
6
7
8
9
10
11
<p>
  <span class="sparkline">
    170,134,115,128,168,166,122,81,56,39,97,114,114,130,151,
    184,148,145,134,145,145,145,143,148,224,181,112,111,129,
    151,131,131,131,114,112,112,112,124,187,202,200,203,237,
    263,221,197,184,185,203,290,330,330,226,113,148,169,148,
    78,96,96,96,77,59,22,22,70,110,128
  </span>
  <span style="color:red"> 128 </span>
  <strong> Glucose </strong>
</p>

With these changes we have the classic Tufte sparkline on our web page. We can even include it within a text paragraph ( 170,134,115,128,168,166,122,81,56,39,97,114,114,130,151, 184,148,145,134,145,145,145,143,148,224,181,112,111,129, 151,131,131,131,114,112,112,112,124,187,202,200,203,237, 263,221,197,184,185,203,290,330,330,226,113,148,169,148, 78,96,96,96,77,59,22,22,70,110,128 128 Glucose ) so that the visualization enhances the content of the text.

Charting Many Variables

By design, sparklines take up very little space on a page, and that makes them ideal for another visualization challenge: showing many variables at once. Of course regular line charts and bar charts can plot multiple data sets simultaneously; however, these multiple series charts rapidly grow unwieldy if the number of data sets exceeds four or five. Some visualization projects show dozens of different variables, far beyond what a multiple series chart can accommodate. A small multiples approach turns the standard chart approach completely around. Instead of showing one chart with multiple data sets, we can show multiple charts, each with a single data set. Placing lots of charts on a page means that each individual chart cannot take up much space. That is the exactly the problem that sparklines solve.

We won’t go too crazy here to keep the code examples manageable, but it’s easy to extend this approach to many more variables. In our case we’ll construct a visualization for analyzing stock market performance. The companies in our analysis will include the 10 largest American companies in 2012, (the “Fortune 10”), Barclay’s best technology stocks for 2012 (as identified in December 2011), and Bristol-Myers Squibb, which CR Magazine named the top company in America for corporate responsibility. Those selections are completely arbitrary, but the example is designed to include three different cases that we will style differently in our visualization. We’ll treat one as a general case (the Fortune 10 list), one as a special class (the Barclay’s list), and one as a unique variable (Bristol-Myers Squibb).

Just as in this chapter’s first example, we need to include the sparklines and jQuery libraries in our web page.

Step 1: Prepare the HTML Markup

The sparklines library makes it easy to embed the data directly inside the HTML markup. For this example, an HTML table is the most appropriate structure for the data. Here’s how such a table could begin. (For brevity’s sake, the following excerpt doesn’t include the full HTML, but the complete example is available in the book’s source code.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<table>
    <thead>
        <tr>
            <th>Symbol</th>
            <th>Company</th>
            <th>2012 Performance</th>
            <th>Gain</th>
        </tr>
    </thead>
    <tbody>
        <tr class='barclays'>
            <td>AAPL</td>
            <td>Apple Inc.</td>
            <td class='sparkline'>418.68,416.11,416.6,443.34,...</td>
            <td>27%</td>
        </tr>
        <tr class='barclays'>
            <td>ALTR</td>
            <td>Altera Corporation</td>
            <td class='sparkline'>37.1,36.92,39.93,39.81,40.43,...</td>
            <td>-7%</td>
        </tr>
        // Markup continues... 
    </tbody>
</table>

The table has three important characteristics relevant to our visualization.

Step 2: Draw the Charts

Just as in this chapter’s first example, creating the sparklines using default options is amazingly simple: it only takes a single line of JavaScript. We use jQuery to select all the elements that contain sparkline data, and we call the sparkline() function to generate the charts. Notice that we only have to make one call to sparkline(), even though each chart has unique data. That’s a major benefit of placing the data within the HTML itself.

1
2
3
$(function() {
    $('.sparkline').sparkline();
}

The resulting charts, shown in figure , all have identical styles, but we’ll fix that in the next few steps.

Symbol Company 2012 Performance Gain
AAPL Apple Inc. 418.68,416.11,416.6,443.34,455.63,489.08,497.7,517.81,540.38,540.37,580.42,590.8,594.27,628.1,599.9,567.94,597.69,560.27,561.72,525.71,557.34,556.05,575.21,569.08,576.98,578.86,600.55,599.64,598.98,580.01,610.28,618.87,645.16,660.2,662.22,677.35,688.14,696.91,664.07,649.62,626.85,607.07,601.25,574.18,547.06,527.68,571.5,585.28,533.25,509.79,519.33,509.59,532.17 27%
ALTR Altera Corporation 37.1,36.92,39.93,39.81,40.43,39.76,39.73,38.55,36.89,37.68,39.18,38.47,39.49,37.85,37.17,34.95,35.26,33.4,33.53,33.03,34.21,32.09,33.29,32.86,33.24,33.64,32.54,31.16,31.04,35.82,35.58,37,36.23,36.23,37.21,37.85,38.03,36.62,33.89,33.32,32.22,32.4,30.3,30.41,30.64,30.45,31.86,32.39,31.58,32.86,34.5,34.01,34.39 (7%)
BMY Bristol Meyers Squibb Co. 32.86,32.46,31.36,31.01,30.98,30.64,31.81,31.31,31.3,31.61,31.87,31.65,32.41,32.67,31.57,33.21,32.32,32.37,32.12,31.74,32.1,32.33,33.34,33.21,34.3,34.88,33.9,34.64,34.69,35.31,31.97,31.07,30.92,31.9,32.33,32.61,32.55,32.92,33.05,33.28,32.73,33.45,33.26,32.82,31.88,31.37,32.27,32.28,32.21,32.38,32.21,31.56,32.24 (2%)
BRKA Berkshire Hathaway Inc. 114500,116520,119775,119211,119800,117980,119190,120000,117434,119065,122190,122170,121900,121295,118385,118580,120925,121950,122795,119850,119500,119845,122000,123375,122670,124945,123898,126625,125730,127735,128479,127175,128880,128225,126560,129942,133000,134584,132700,135555,132502,133841,129725,130550,127585,129345,132606,131916,131090,133795,134800,133000,134060 17%
COP ConocoPhillips 52.95,51.26,51.89,50.58,51.35,52.65,53.95,55.85,57.1,56.74,56.76,56.26,55.9,55.42,54.15,53.59,52.93,51.29,51.61,49.65,50.91,50.01,52.73,54.18,52.18,54.59,53.49,53.71,55.34,54.34,55.07,56.62,56.73,55.71,56.14,55.99,57.54,56.7,56.52,56.92,56.17,57.45,57.31,57.65,55.67,55.03,56.67,56.94,57.94,57.69,58.61,57.07,57.99 10%
CTXS Citrix Systems, Inc. 62.02,64.85,68.12,65.14,68.78,71.46,74.77,75.16,75.43,76.27,78.2,77.68,78.91,78.28,75.12,77.78,85.78,82.85,79.46,74.82,75.43,70.79,77.12,80.12,80.14,83.94,77.45,76.63,80.75,77.81,72.44,76.26,78.08,77,77.69,80.59,81.74,81.19,76.53,71.57,67.7,64.16,62.82,62.77,60.17,59.21,61.77,61.16,62.03,65.02,66.15,64.3,65.62 6%
CVX Chevron Corporation 104.78,102.63,103.4,100.57,102.06,101.85,103.97,106.33,106.85,106.81,107.5,103.68,104.51,102.11,98.24,99.94,103.52,101.11,100.1,96.84,97.24,94.83,99.18,102.62,98.79,103.77,103.34,104.27,107.4,107.47,109.3,111.69,111.7,111.05,111.2,113.03,116.25,116.79,115.56,116.5,111.11,112.41,110.23,107.44,104.94,102.4,105.47,105.69,106.99,107.82,109.71,106.45,108.14 3%
F Ford Motor Company 11.5,11.82,12.36,12.03,12.6,12.26,12.57,12.05,12.54,12.4,12.33,12.14,12.3,12.29,11.75,11.24,11.43,10.56,10.47,9.91,10.49,10.02,10.55,10.24,10.09,9.49,9.4,9.18,9.12,8.91,9.05,9.3,9.58,9.44,9.29,10.09,10.48,10.35,9.81,10.11,10.07,10.13,10.31,11.17,10.93,10.5,11.1,11.45,11.48,11.1,11.86,12.87,12.95 13%
FNMA Federal National Mortgage Association 0.2,0.23,0.24,0.23,0.24,0.3,0.38,0.32,0.32,0.32,0.31,0.31,0.29,0.28,0.27,0.29,0.28,0.27,0.3,0.27,0.27,0.26,0.26,0.26,0.26,0.26,0.25,0.24,0.24,0.24,0.24,0.28,0.24,0.25,0.25,0.24,0.28,0.28,0.28,0.28,0.27,0.27,0.27,0.27,0.28,0.27,0.28,0.27,0.27,0.27,0.26,0.26,0.26 30%
GE General Electric Company 18.03,18.21,18.51,18.39,18.38,18.25,18.64,18.76,18.5,18.57,19.7,19.29,19.57,19.01,18.41,18.88,19.29,18.86,18.54,18.48,18.72,18.08,18.72,19.5,19.48,20.5,19.67,19.44,19.54,20.57,20.61,20.75,20.65,20.46,20.37,21.23,21.74,22.33,22.5,22.91,22.28,21.83,20.92,21.12,20.81,19.97,20.85,20.94,21.27,21.42,20.88,20.44,20.99 16%
GLW Corning Incorporated 13.18,13.64,14.05,12.3,13.23,13.25,13.49,13.4,12.73,13.02,14.04,13.74,13.8,13.27,13.31,12.91,14.16,13.39,13.04,12.49,12.65,12.43,12.94,12.82,12.67,12.74,12.61,12.14,12.09,11.36,11.18,11.4,11.82,11.35,11.89,12.45,13.01,13.11,13.04,13.19,12.88,13.41,11.72,11.61,11.25,10.9,11.29,12.23,12.54,12.62,12.6,12.47,12.62 (4%)
GM General Motors Company 22.92,24.29,25,24.37,26.18,25.5,27.34,26.07,26.45,25.62,25.57,25.17,25.65,24.81,23.8,23.6,23.53,22.36,22,21.18,22.44,22.01,22.05,21.74,20.6,19.72,20.31,19.62,19.36,19.67,20.04,20.54,22.01,21.18,21.35,23.37,24.14,24.8,22.75,24.8,24.44,24.59,23.28,25.79,25.04,23.85,25.21,25.88,25.19,24.61,27.32,27.85,28.83 26%
HPQ Hewlett-Packard Company 25.67,25.76,27.35,27.11,28.27,27.91,28.77,25.9,24.62,23.51,23.93,23.09,23.29,22.58,24.01,23.95,24.19,23.72,22.62,20.97,21.82,20.77,21.8,21.27,20.02,19.77,19.24,18.66,18.29,18.25,17.95,19.37,19.19,17.28,16.59,17.12,18,17.42,16.9,14.59,14.27,14.34,13.96,13.63,13.48,12.73,12.32,12.87,13.81,14.75,14.34,13.68,14.25 (44%)
QCOM QUALCOMM, Inc. 55.28,55.65,56.83,56.88,60.1,60.76,61.54,62.45,61.66,63.14,64.61,65.86,67.22,66.36,65.85,61.49,63.39,61.15,61.1,55.29,56.62,54.68,58.29,56.05,55.19,55.23,54.87,54.54,57.22,58.86,59.76,61.48,62.78,61.93,60.97,61.69,64.62,64.02,62.22,62.39,58.66,58.52,58.81,59.07,61.38,61.69,62.88,63.37,63.86,59.83,61.61,60.64,61.86 12%
TER Teradyne, Inc. 14.63,14.84,16.14,16.95,17.03,16.5,17.01,16.38,16.11,16.21,17.05,16.82,16.89,16.42,16.29,16.3,17.39,16.13,15.71,14.32,14.79,13.61,14.38,14.46,14.31,14.06,13.67,13.21,13.64,14.97,14.8,15.59,15.94,15.74,15.62,16.28,16.43,14.6,14.22,14.07,13.62,14.12,14.5,15.31,15.51,15.03,15.93,15.64,15.84,16.49,16.75,16.42,16.89 15%
TSLA Tesla Motors Inc 26.91,22.79,26.6,29.33,31.15,31.1,34.97,33.75,34.04,34.74,35.32,34.08,37.24,34.48,33.59,33.16,33.34,31.83,32.25,27.56,29.81,28.15,30.08,29.91,33.79,31.29,30.99,34.25,31.79,29.51,27.27,29.94,30.01,29.5,28.52,29.35,30.39,30.02,29.28,28.89,27.64,27.74,27.38,28.92,30.32,31.84,32.13,33.82,34.17,33.81,34,33.22,33.87 26%
WMT Wal-Mart Stores, Inc. 57.58,58.11,59.54,59.25,60.54,60.41,60.98,57.38,57.59,59.03,59.77,59.68,60.13,59.61,58.72,61.35,57.99,57.67,58.77,61.75,64.6,64.84,67.48,67.01,66.57,68.96,70.58,72.38,71.46,73.71,73.74,73.27,71.59,71.71,72.2,73.41,74.09,74.04,73.39,74.72,75.39,75.2,74.7,72.37,71.91,67.65,69.81,71.62,72.29,68.75,68.65,67.61,68.23 18%
XOM Exxon Mobil Corporation 83.01,82.78,85.33,83.71,82.82,82.17,83.96,85.65,84.66,82.67,84.76,83.89,85.05,83.17,81.34,83.65,84.41,82.93,82.05,80.44,81.04,76.94,79.82,82.17,81.07,84.49,83.73,84.39,84.86,86.34,86.44,87.89,87.85,87.5,86.76,89.36,91.73,91.35,90.88,91.97,90.46,91.58,90.06,89.71,87.21,86.45,89.09,88.14,88.6,88.08,87.23,85.1,86.55 4%
Sparklines can be a good visualization to include within page elements such as tables.

Step 3: Establish a Default Style for the Charts

If we don’t like the sparklines library’s default style, it’s easy to change it using an options object, as shown next. The object is the second parameter to the sparkline() function, and here it changes the color for the charts and disables the highlights on the minimum, maximum, and final values. The first parameter, the string 'html', indicates to the library that the data is already present in our HTML.

1
2
3
4
5
6
7
$('.sparkline').sparkline('html',{
    lineColor: "#006363",
    fillColor: "#2D9999",
    spotColor: false,
    minSpotColor: false,
    maxSpotColor: false
});

Figure shows the result for one row. We’ll use this style as the default for all our charts.

Symbol Company 2012 Performance Gain
AAPL Apple Inc. 418.68,416.11,416.6,443.34,455.63,489.08,497.7,517.81,540.38,540.37,580.42,590.8,594.27,628.1,599.9,567.94,597.69,560.27,561.72,525.71,557.34,556.05,575.21,569.08,576.98,578.86,600.55,599.64,598.98,580.01,610.28,618.87,645.16,660.2,662.22,677.35,688.14,696.91,664.07,649.62,626.85,607.07,601.25,574.18,547.06,527.68,571.5,585.28,533.25,509.79,519.33,509.59,532.17 27%
The sparkline options let us adjust the chart styles.

Step 4: Modify the Default Style for Special Classes

With a default style in place, we can turn our attention to the special class of charts for stocks in Barclay’s technology list. For our example, let’s change the color of the chart without any other changes to our default style. That final clause is important. We could just copy-and-paste the options, but that would be setting ourselves up for problems in the future. You can see why in the following example code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$('tr:not(.barclays) .sparkline').sparkline('html',{
    lineColor: "#006363",
    fillColor: "#2D9999",
    spotColor: false,
    minSpotColor: false,
    maxSpotColor: false
});
$('tr.barclays .sparkline').sparkline('html',{
    lineColor: "#A50000",
    fillColor: "#FE4C4C",
    spotColor: false,
    minSpotColor: false,
    maxSpotColor: false
});

Notice that the second call to sparklines() duplicates options from the first call that haven’t changed, specifically for the spot colors. This makes the code harder to maintain if, in the future, we decide to turn spot colors back on for all our charts, since we would have to make changes to our code in two places. There is a better way.

To avoid duplication we first define a variable that holds our default options.

1
2
3
4
5
6
7
var sparkline_default = {
    lineColor: "#006363",
    fillColor: "#2D9999",
    spotColor: false,
    minSpotColor: false,
    maxSpotColor: false
};

Next we create a new variable for the Barclay’s styles. To create this new variable, we can use the jQuery .extend() function to avoid duplication. In the following code, we pass three parameters to .extend(). The first parameter is the target. It’s an object that the function will modify, and we start with an empty object ({}). The next parameters are objects that .extend() will merge into the target. The merge process adds new properties to the target and updates any properties in the target object with new values. Since we’re passing two additional parameters, we’re asking for two merges.

1
2
3
4
var sparkline_barclays = $.extend( {}, sparkline_default, {
    lineColor: "#A50000",
    fillColor: "#FE4C4C"
}); 

You can think of the call to .extend() as a two-stage process.

  1. Since our target is initially empty, the first merge will add all of the properties from sparkline_default to the target.
  2. Our target now has the same properties as sparkline_default, and the second merge will modify it by updating the two properties in the last parameter, lineColor and fillColor.

The resulting object will hold the options we want for charts of Barclay’s technology stocks. Here’s a complete code listing, using these objects to create the charts.

1
2
3
4
5
6
7
8
9
10
11
12
13
var sparkline_default = {
    lineColor: "#006363",
    fillColor: "#2D9999",
    spotColor: false,
    minSpotColor: false,
    maxSpotColor: false
};
var sparkline_barclays = $.extend( {}, sparkline_default, {
    lineColor: "#A50000",
    fillColor: "#FE4C4C"
}); 
$('tr:not(.barclays) .sparkline').sparkline('html',sparkline_default);
$('tr.barclays .sparkline').sparkline('html',sparkline_barclays);

Notice in line 12 that we create the non-technology sparklines by selecting table rows (<tr>) that don’t have the "barclays" class. In line 13 we create the technology sparklines. Because we’ve defined the technology options based on the default, we have an easy way to maintain both default styles and styles for special classes. The chart colors in figure clearly distinguish the stock types in our table.

Symbol Company 2012 Performance Gain
TSLA Tesla Motors Inc 26.91,22.79,26.6,29.33,31.15,31.1,34.97,33.75,34.04,34.74,35.32,34.08,37.24,34.48,33.59,33.16,33.34,31.83,32.25,27.56,29.81,28.15,30.08,29.91,33.79,31.29,30.99,34.25,31.79,29.51,27.27,29.94,30.01,29.5,28.52,29.35,30.39,30.02,29.28,28.89,27.64,27.74,27.38,28.92,30.32,31.84,32.13,33.82,34.17,33.81,34,33.22,33.87 26%
WMT Wal-Mart Stores, Inc. 57.58,58.11,59.54,59.25,60.54,60.41,60.98,57.38,57.59,59.03,59.77,59.68,60.13,59.61,58.72,61.35,57.99,57.67,58.77,61.75,64.6,64.84,67.48,67.01,66.57,68.96,70.58,72.38,71.46,73.71,73.74,73.27,71.59,71.71,72.2,73.41,74.09,74.04,73.39,74.72,75.39,75.2,74.7,72.37,71.91,67.65,69.81,71.62,72.29,68.75,68.65,67.61,68.23 18%
Different visual styles distinguish different types of data.

Step 5: Create a Unique Style for a Specific Chart

For the final step in this example, let’s consider the single stock at the top of CR Magazine’s list. Suppose we want to add distinct styles to its chart, and we know those styles only when we’re generating the HTML, not when we’re writing the JavaScript. How can we adjust the chart style if we can’t modify any JavaScript?

Sparklines let you add special attributes directly to the HTML element containing a chart. To set the line color, for example, you need to specify the attribute sparkLineColor. The problem is that if we were to enter this attribute directly in the HTML, the result would’t be valid HTML, because the HTML specification doesn’t recognize the sparkLineColor attribute. To conform to the HTML standard, custom attributes must have names that begin with the prefix data-.

To use HTML-compliant names to refer to sparklines’ custom attributes, we just need to tell the sparklines library how to find those names. For our HTML, we use the standard data- prefix instead of spark in line 4.

1
2
3
4
5
6
7
8
<tr>
   <td>BMY</td>
   <td>Bristol Meyers Squibb Co.</td>
   <td class='sparkline' data-LineColor='#679A00' data-FillColor='#B6ED47'>
      32.86,32.46,31.36,...
   </td>
   <td>(2%)</td>
</tr>

Now we have to add a couple more options in our call to sparkline(). First we set enableTagOptions to true to tell the library that we’re including options directly in the HTML. Then we set tagOptionsPrefix to "data-" to specify the specific prefix we’re using for those attributes.

Note: As of this writing, the jQuery sparklines documentation for tagOptionsPrefix is not correct. The documentation lists the option as tagOptionPrefix, where “option” is singular instead of plural. The library’s code, however, expects the plural form.

If we use these options correctly, one of our charts will have the distinct color in figure .

BMY Bristol Meyers Squibb Co. 32.86,32.46,31.36,31.01,30.98,30.64,31.81,31.31,31.3,31.61,31.87,31.65,32.41,32.67,31.57,33.21,32.32,32.37,32.12,31.74,32.1,32.33,33.34,33.21,34.3,34.88,33.9,34.64,34.69,35.31,31.97,31.07,30.92,31.9,32.33,32.61,32.55,32.92,33.05,33.28,32.73,33.45,33.26,32.82,31.88,31.37,32.27,32.28,32.21,32.38,32.21,31.56,32.24 (2%)
The sparklines library supports unique styling options for individual charts.

To pass the appropriate options to sparkline() we can take advantage of the work we did in step 5. Since we created a special object for default options, that’s the only object we have to change.

1
2
3
4
5
6
7
8
9
var sparkline_default = {
    lineColor: "#006363",
    fillColor: "#2D9999",
    spotColor: false,
    minSpotColor: false,
    maxSpotColor: false,
    enableTagOptions: true,
    tagOptionsPrefix: "data-"
};

We only need to make the change in one place, and all of our calls to sparkline()use the new options. Here is the final, complete JavaScript code for this example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$(function() {
    var sparkline_default = {
        lineColor: "#006363",
        fillColor: "#2D9999",
        spotColor: false,
        minSpotColor: false,
        maxSpotColor: false,
        enableTagOptions: true,
        tagOptionsPrefix: "data-"
    };
    var sparkline_barclays = $.extend( {}, sparkline_default, {
        lineColor: "#A50000",
        fillColor: "#FE4C4C"
    }); 
    $('tr:not(.barclays) .sparkline').sparkline('html',sparkline_default);
    $('tr.barclays .sparkline').sparkline('html',sparkline_barclays);
}

And figure shows the final result. We have a table that integrates text and charts, and we can style those charts appropriately and efficiently for the default case, for a special class, and for a unique value.

Symbol Company 2012 Performance Gain
AAPL Apple Inc. 418.68,416.11,416.6,443.34,455.63,489.08,497.7,517.81,540.38,540.37,580.42,590.8,594.27,628.1,599.9,567.94,597.69,560.27,561.72,525.71,557.34,556.05,575.21,569.08,576.98,578.86,600.55,599.64,598.98,580.01,610.28,618.87,645.16,660.2,662.22,677.35,688.14,696.91,664.07,649.62,626.85,607.07,601.25,574.18,547.06,527.68,571.5,585.28,533.25,509.79,519.33,509.59,532.17 27%
ALTR Altera Corporation 37.1,36.92,39.93,39.81,40.43,39.76,39.73,38.55,36.89,37.68,39.18,38.47,39.49,37.85,37.17,34.95,35.26,33.4,33.53,33.03,34.21,32.09,33.29,32.86,33.24,33.64,32.54,31.16,31.04,35.82,35.58,37,36.23,36.23,37.21,37.85,38.03,36.62,33.89,33.32,32.22,32.4,30.3,30.41,30.64,30.45,31.86,32.39,31.58,32.86,34.5,34.01,34.39 (7%)
BMY Bristol Meyers Squibb Co. 32.86,32.46,31.36,31.01,30.98,30.64,31.81,31.31,31.3,31.61,31.87,31.65,32.41,32.67,31.57,33.21,32.32,32.37,32.12,31.74,32.1,32.33,33.34,33.21,34.3,34.88,33.9,34.64,34.69,35.31,31.97,31.07,30.92,31.9,32.33,32.61,32.55,32.92,33.05,33.28,32.73,33.45,33.26,32.82,31.88,31.37,32.27,32.28,32.21,32.38,32.21,31.56,32.24 (2%)
BRKA Berkshire Hathaway Inc. 114500,116520,119775,119211,119800,117980,119190,120000,117434,119065,122190,122170,121900,121295,118385,118580,120925,121950,122795,119850,119500,119845,122000,123375,122670,124945,123898,126625,125730,127735,128479,127175,128880,128225,126560,129942,133000,134584,132700,135555,132502,133841,129725,130550,127585,129345,132606,131916,131090,133795,134800,133000,134060 17%
COP ConocoPhillips 52.95,51.26,51.89,50.58,51.35,52.65,53.95,55.85,57.1,56.74,56.76,56.26,55.9,55.42,54.15,53.59,52.93,51.29,51.61,49.65,50.91,50.01,52.73,54.18,52.18,54.59,53.49,53.71,55.34,54.34,55.07,56.62,56.73,55.71,56.14,55.99,57.54,56.7,56.52,56.92,56.17,57.45,57.31,57.65,55.67,55.03,56.67,56.94,57.94,57.69,58.61,57.07,57.99 10%
CTXS Citrix Systems, Inc. 62.02,64.85,68.12,65.14,68.78,71.46,74.77,75.16,75.43,76.27,78.2,77.68,78.91,78.28,75.12,77.78,85.78,82.85,79.46,74.82,75.43,70.79,77.12,80.12,80.14,83.94,77.45,76.63,80.75,77.81,72.44,76.26,78.08,77,77.69,80.59,81.74,81.19,76.53,71.57,67.7,64.16,62.82,62.77,60.17,59.21,61.77,61.16,62.03,65.02,66.15,64.3,65.62 6%
CVX Chevron Corporation 104.78,102.63,103.4,100.57,102.06,101.85,103.97,106.33,106.85,106.81,107.5,103.68,104.51,102.11,98.24,99.94,103.52,101.11,100.1,96.84,97.24,94.83,99.18,102.62,98.79,103.77,103.34,104.27,107.4,107.47,109.3,111.69,111.7,111.05,111.2,113.03,116.25,116.79,115.56,116.5,111.11,112.41,110.23,107.44,104.94,102.4,105.47,105.69,106.99,107.82,109.71,106.45,108.14 3%
F Ford Motor Company 11.5,11.82,12.36,12.03,12.6,12.26,12.57,12.05,12.54,12.4,12.33,12.14,12.3,12.29,11.75,11.24,11.43,10.56,10.47,9.91,10.49,10.02,10.55,10.24,10.09,9.49,9.4,9.18,9.12,8.91,9.05,9.3,9.58,9.44,9.29,10.09,10.48,10.35,9.81,10.11,10.07,10.13,10.31,11.17,10.93,10.5,11.1,11.45,11.48,11.1,11.86,12.87,12.95 13%
FNMA Federal National Mortgage Association 0.2,0.23,0.24,0.23,0.24,0.3,0.38,0.32,0.32,0.32,0.31,0.31,0.29,0.28,0.27,0.29,0.28,0.27,0.3,0.27,0.27,0.26,0.26,0.26,0.26,0.26,0.25,0.24,0.24,0.24,0.24,0.28,0.24,0.25,0.25,0.24,0.28,0.28,0.28,0.28,0.27,0.27,0.27,0.27,0.28,0.27,0.28,0.27,0.27,0.27,0.26,0.26,0.26 30%
GE General Electric Company 18.03,18.21,18.51,18.39,18.38,18.25,18.64,18.76,18.5,18.57,19.7,19.29,19.57,19.01,18.41,18.88,19.29,18.86,18.54,18.48,18.72,18.08,18.72,19.5,19.48,20.5,19.67,19.44,19.54,20.57,20.61,20.75,20.65,20.46,20.37,21.23,21.74,22.33,22.5,22.91,22.28,21.83,20.92,21.12,20.81,19.97,20.85,20.94,21.27,21.42,20.88,20.44,20.99 16%
GLW Corning Incorporated 13.18,13.64,14.05,12.3,13.23,13.25,13.49,13.4,12.73,13.02,14.04,13.74,13.8,13.27,13.31,12.91,14.16,13.39,13.04,12.49,12.65,12.43,12.94,12.82,12.67,12.74,12.61,12.14,12.09,11.36,11.18,11.4,11.82,11.35,11.89,12.45,13.01,13.11,13.04,13.19,12.88,13.41,11.72,11.61,11.25,10.9,11.29,12.23,12.54,12.62,12.6,12.47,12.62 (4%)
GM General Motors Company 22.92,24.29,25,24.37,26.18,25.5,27.34,26.07,26.45,25.62,25.57,25.17,25.65,24.81,23.8,23.6,23.53,22.36,22,21.18,22.44,22.01,22.05,21.74,20.6,19.72,20.31,19.62,19.36,19.67,20.04,20.54,22.01,21.18,21.35,23.37,24.14,24.8,22.75,24.8,24.44,24.59,23.28,25.79,25.04,23.85,25.21,25.88,25.19,24.61,27.32,27.85,28.83 26%
HPQ Hewlett-Packard Company 25.67,25.76,27.35,27.11,28.27,27.91,28.77,25.9,24.62,23.51,23.93,23.09,23.29,22.58,24.01,23.95,24.19,23.72,22.62,20.97,21.82,20.77,21.8,21.27,20.02,19.77,19.24,18.66,18.29,18.25,17.95,19.37,19.19,17.28,16.59,17.12,18,17.42,16.9,14.59,14.27,14.34,13.96,13.63,13.48,12.73,12.32,12.87,13.81,14.75,14.34,13.68,14.25 (44%)
QCOM QUALCOMM, Inc. 55.28,55.65,56.83,56.88,60.1,60.76,61.54,62.45,61.66,63.14,64.61,65.86,67.22,66.36,65.85,61.49,63.39,61.15,61.1,55.29,56.62,54.68,58.29,56.05,55.19,55.23,54.87,54.54,57.22,58.86,59.76,61.48,62.78,61.93,60.97,61.69,64.62,64.02,62.22,62.39,58.66,58.52,58.81,59.07,61.38,61.69,62.88,63.37,63.86,59.83,61.61,60.64,61.86 12%
TER Teradyne, Inc. 14.63,14.84,16.14,16.95,17.03,16.5,17.01,16.38,16.11,16.21,17.05,16.82,16.89,16.42,16.29,16.3,17.39,16.13,15.71,14.32,14.79,13.61,14.38,14.46,14.31,14.06,13.67,13.21,13.64,14.97,14.8,15.59,15.94,15.74,15.62,16.28,16.43,14.6,14.22,14.07,13.62,14.12,14.5,15.31,15.51,15.03,15.93,15.64,15.84,16.49,16.75,16.42,16.89 15%
TSLA Tesla Motors Inc 26.91,22.79,26.6,29.33,31.15,31.1,34.97,33.75,34.04,34.74,35.32,34.08,37.24,34.48,33.59,33.16,33.34,31.83,32.25,27.56,29.81,28.15,30.08,29.91,33.79,31.29,30.99,34.25,31.79,29.51,27.27,29.94,30.01,29.5,28.52,29.35,30.39,30.02,29.28,28.89,27.64,27.74,27.38,28.92,30.32,31.84,32.13,33.82,34.17,33.81,34,33.22,33.87 26%
WMT Wal-Mart Stores, Inc. 57.58,58.11,59.54,59.25,60.54,60.41,60.98,57.38,57.59,59.03,59.77,59.68,60.13,59.61,58.72,61.35,57.99,57.67,58.77,61.75,64.6,64.84,67.48,67.01,66.57,68.96,70.58,72.38,71.46,73.71,73.74,73.27,71.59,71.71,72.2,73.41,74.09,74.04,73.39,74.72,75.39,75.2,74.7,72.37,71.91,67.65,69.81,71.62,72.29,68.75,68.65,67.61,68.23 18%
XOM Exxon Mobil Corporation 83.01,82.78,85.33,83.71,82.82,82.17,83.96,85.65,84.66,82.67,84.76,83.89,85.05,83.17,81.34,83.65,84.41,82.93,82.05,80.44,81.04,76.94,79.82,82.17,81.07,84.49,83.73,84.39,84.86,86.34,86.44,87.89,87.85,87.5,86.76,89.36,91.73,91.35,90.88,91.97,90.46,91.58,90.06,89.71,87.21,86.45,89.09,88.14,88.6,88.08,87.23,85.1,86.55 4%
A complete example distinguishes different individual data sets in a larger collection.

The third example in chapter 2 uses a full-featured charting package for a similar result. If you don’t need the space efficiency of sparklines, consider that approach as an alternative.

Annotating Sparklines

Because they’re designed to maximize information density, sparklines omit many traditional chart components such as axes and labels. This approach certainly focuses on the data itself, but it can sometimes leave users without enough context to understand the data. Print versions usually rely on traditional text to supply this context, but on the web we have more flexibility. We can present the data by itself in a sparkline, and we can give users the chance to explore the data’s context through interactions.

Tooltips, which show additional information as users hover their mouse pointers over sections of a web page, can be an effective way to annotate a sparkline, so long as the users are accessing the page from a desktop computer. (Touch-based devices such as smartphones and tablets don’t typically support the concept of hover.) We’ll walk through a visualization that includes tooltips in this example; other examples in the chapter consider alternative approaches that may be more effective for touch devices.

Let’s see how we can use a customized form of tooltips by enhancing the charts in the previous example.

Just as in this chapter’s first example, we need to include the sparklines and jQuery libraries in our web page.

Step 1: Prepare the Data

In the previous examples we’ve embedded the data directly in the HTML markup. That’s convenient since it lets us separate the data from our code. In this example, however, the JavaScript code will need more detailed knowledge of the data so it can present the right tooltip information. This time we’ll use a JavaScript array to store our data, so that all the relevant information is in one place. For this example we can focus on a single stock. And even though we’re graphing only the adjusted closing price, the array will track additional data that we can include in the tooltips. Here’s an excerpt of the data for one of the stocks.

1
2
3
4
5
6
7
8
var stock = [
  { date: "2012-01-03", open: 409.40, high: 422.75, low: 409.00, 
    close: 422.40, volume: 10283900, adj_close: 416.26 },
  { date: "2012-01-09", open: 425.50, high: 427.75, low: 418.66, 
    close: 419.81, volume:  9327900, adj_close: 413.70 },
  { date: "2012-01-17", open: 424.20, high: 431.37, low: 419.75, 
    close: 420.30, volume: 10673200, adj_close: 414.19 },
  // Data set continues...

Step 2: Prepare the HTML Markup

Our visualization will include three distinct areas, each in a <div> element. The primary <div> created in line 3 will hold the chart. Underneath the chart we’ll add the primary tool tip information in its own <div> (line 4), and we’ll include supplementary details to the right (line 7). The following example uses inline styles for clarity; a production site might prefer to use CSS style sheets.

1
2
3
4
5
6
7
8
9
<div id="stock">
    <div style="float:left">
        <div class="chart"></div>
        <div class="info"></div>
    </div>
    <div style="float:left">
        <div class="details"></div>
    </div>
</div>

Step 3: Add the Chart

Adding the chart to our markup is easy with the sparklines library. We can use the jQuery .map() function to extract the adjusted close value from our stock array. The minSpotColor and maxSpotColor options tell the library how to highlight the lowest and highest values for the year.

1
2
3
4
5
6
7
8
9
10
$('#stock .chart').sparkline(
    $.map(stock, function(wk) { return wk.adj_close; }),
    {
        lineColor: "#006363",
        fillColor: "#2D9999",
        spotColor: false,
        minSpotColor: "#CA0000",
        maxSpotColor: "#CA0000"
    }
);

The static chart of figure shows the stock performance nicely.

 
A static sparkline shows the change in the data set over time.

Step 4: Add the Primary Annotation

The sparklines library adds a simple tooltip to all of its charts by default. Although that tooltip shows the value over which the user’s mouse is hovering, the presentation isn’t particularly elegant, and, more importantly, it doesn’t provide as much information as we would like. Let’s enhance the default behavior to meet our needs.

Looking at the library’s defaults, we can retain the vertical marker, but we don’t want the default tooltip. Adding the option disableTooltips with a value of true will turn off the undesired tooltip.

For our own custom tooltip, we can rely on a handy feature of the sparklines library. The library generates a custom event whenever the user’s mouse moves over a chart region. That event is the "sparklineRegionChange" event. The library attaches a custom property, sparklines, to those events. By analyzing that property we can determine the mouse’s location relative to the data.

1
2
3
4
5
6
$(".chart")
    .on('sparklineRegionChange', function(ev) {
        var idx = ev.sparklines[0].getCurrentRegionFields().offset;
        /* if it's defined, idx has the index into the
           data array corresponding to the mouse pointer */
    });

As the comment in lines 4 and 5 indicate, the library sometimes generates the event when the mouse leaves the chart area. In those cases a defined value for the offset will not exist.

Once we have the mouse position, we can place our tooltip information in the <div> we set aside for it. We get the information in lines 3 and 5 from the stock array using the index value from the sparklineRegionChange event.

1
2
3
4
5
6
        if (idx) {
            $(".info").html(
                "Week of " + stock[idx].date 
              + "&nbsp;&nbsp;&nbsp; "
              + "Close: $" + stock[idx].adj_close);
        }

The sparklines library isn’t completely reliable in generating events when the mouse leaves the chart area. Instead of using the custom event, therefore, we can use the standard JavaScript mouseout event. When the user moves the mouse off the chart, we’ll turn off the custom tooltip by setting it’s content to a blank space. We use the HTML non-breaking space (&nbsp;) so the browser doesn’t think the <div> is completely empty. If we used a standard space character, the browser would treat the <div> as empty and recalculate the height of the page, causing an annoying jump in the page contents. (For the same reason we should initialize that <div> with &nbsp; instead of leaving it blank.)

1
2
3
    .on('mouseout', function() {
        $(".info").html("&nbsp;");
    });

For the cleanest implementation, we combine all of these steps using method chaining. (To keep it concise, the excerpt below omits the chart styling options.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$('#stock .chart')
    .sparkline(
        $.map(stock, function(wk) { return wk.adj_close; }),
        { disableTooltips: true }
    ).on('sparklineRegionChange', function(ev) {
        var idx = ev.sparklines[0].getCurrentRegionFields().offset;
        if (idx) {
            $(".info").html(
                "Week of " + stock[idx].date 
              + "&nbsp;&nbsp;&nbsp; "
              + "Close: $" + stock[idx].adj_close);
        }
    }).on('mouseout', function() {
        $(".info").html("&nbsp;");
    });

Now with figure we have a nice, interactive tooltip that tracks the user’s mouse as it moves across the chart.

 
An interactive sparkline tracks the user’s mouse and provides information relevant to the mouse position.

Step 5: Provide Additional Information

The tooltip information we’ve added so far shows the immediately relevant information to the user: the week and the adjusted closing price of the stock. Our data, however, contains additional information that might be useful to the user. We can expand on the original tooltip by displaying that information as well.

At the same time we update the primary tooltip region, let’s add the extra data.

1
2
3
4
5
6
$(".details").html(
    "Open: $" + stock[idx].open + "<br/>"
  + "High: $" + stock[idx].high + "<br/>"
  + "Low: $"  + stock[idx].low  + "<br/>"
  + "Volume: " + stock[idx].volume
);

When we clear the primary tooltip region, we’ll clear this area as well.

1
$(".details").html("");

Because it won’t affect the vertical size of the page, we don’t need to fill this <div> with a dummy &nbsp;.

 
Interactive sparklines can show additional information in many ways.

With figure we have the visualization we want. The chart clearly shows the overall trend for the stock during the year, but it takes up only a small amount of space on the web page. At first glance the chart is also free of distracting elements such as labels and axes. For users who just want a general sense of the stock’s performance, those elements are superfluous. Users who want the full details need only hover their mouse over the chart and it reveals the complete market information.

Because we’ve managed to display the information while retaining the compact nature of sparklines, the technique in this example works well when combined with the small multiples approach of this chapter’s second example.

The next example includes an alternate method for showing the extra details.

Drawing Composite Charts

So far in this chapter we’ve seen how sparklines can provide a lot of visual information in a very small space. That characteristic makes them perfect for integrating charts in a complete web page that includes text, tables, and other elements. We haven’t yet exhausted the capabilities of sparklines, however. We can increase the information density of our visualizations still further by creating composite charts—in effect, drawing multiple charts in the same space.

To see an example of this technique, we can build on the previous example. In that example we used a sparkline to show the closing price of a stock over an entire year. Price is indeed the most relevant data about a stock, but there’s another quantity that many investors like to see: the stock’s trading volume. And just as with price, it can be important to understand the trend for trading volume at a glance. That makes the value an excellent candidate for a chart.

Just as in this chapter’s first example, we need to include the sparklines and jQuery libraries in our web page. Because we’re visualizing the same data as in the previous example, we’ll also want to set up the data array and the HTML markup exactly as in that example.

Step 1: Draw the Trading Volume Chart

Even though we’re including a chart of trading volume, the most important quantity is the stock price. To keep the emphasis on stock price, we want to draw that chart on top of the chart for trading volume. That means we need to draw the trading volume chart first.

The code for trading volume is very similar to the stock price from the previous example. Instead of an area chart, however, we’ll use a bar chart. We use the jQuery .map() function to extract the volume property from our data array. Setting the type option to "bar" in line 3 is all it takes to tell the sparklines library to create a bar chart.

1
2
3
4
$('#stock .chart').sparkline(
    $.map(stock, function(wk) { return wk.volume; }),
    { type: "bar" }
);

Figure shows the results.

 
The sparklines library can create bar charts as well as line charts.

Step 2: Add the Closing Price Chart

To add the price chart on top of the volume chart, we can call the sparklines library once again. We give it the same containing element and, most importantly, set the composite option to true in line 10. This parameter tells the library not to erase any existing chart in the element but to simply draw over it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$('#stock .chart')
    .sparkline(
        $.map(stock, function(wk) { return wk.volume; }),
        { 
            type: "bar" 
        }
    ).sparkline(
        $.map(stock, function(wk) { return wk.adj_close; }),
        {
            composite: true,
            lineColor: "#006363",
            fillColor: "rgba(45, 153, 153, 0.3)",
            disableTooltips: true
        }
    );

Notice the way we specify the fill color for the second chart. We set a transparency (or alpha) value of 0.3. This value makes the chart area nearly transparent, so the volume chart will show through. Note, though, that some older web browsers, notably Internet Explorer version 8 and earlier, do not support the transparency standard. If your site has a significant number of users with those browsers, you might consider simply setting the fillColor option to false, which will disable filling the area entirely.

As figure shows, the result combines both charts in the same space.

 
Multiple charts may be combined in the same space.

Step 3: Add the Annotations

We can add annotations to the chart using the same approach as in the previous example. Because our charts now include the trading volume, it’s appropriate to move that value from the details area into the primary annotation <div>. The code to do that is a simple adjustment from the prior example.

In addition to moving the text from one area to the other, we make two significant changes.

  1. We get the idx value from the second element of the event’s sparklines array (sparklines[1]) in line 2. That’s because the first element of that array is the first chart. The sparklines library doesn’t really return any useful information about bar charts in the sparklineRegionChange event. Fortunately, we can get all the information we need from the line chart.

  2. We show the trading volume in millions, rounded to two decimal places. The calculation is in line 8. It’s much easier for our users to quickly grasp “24.4M” than “24402100.”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    .on('sparklineRegionChange', function(ev) {
        var idx = ev.sparklines[1].getCurrentRegionFields().offset;
        if (idx) {
            $(".info").html(
              "Week of " + stock[idx].date 
            + "&nbsp;&nbsp;&nbsp; Close: $" + stock[idx].adj_close
            + "&nbsp;&nbsp;&nbsp; Volume: " 
            + Math.round(stock[idx].volume/10000)/100 + "M"
            );
            $(".details").html(
                "Open: $" + stock[idx].open + "<br/>"
              + "High: $" + stock[idx].high + "<br/>"
              + "Low: $"  + stock[idx].low
            );
        }

As in the previous example, the annotations in our chart (shown in figure ) provide additional details.

 
Tracking the mouse position makes it possible to interactively annotate the charts.

Step 4: Show Details as a Chart

So far we’ve shown the additional details for the stock (open, close, high, and low) as text values. As long as we’re drawing multiple charts, we can show those values graphically as well. The statistical box plot serves as a useful model for us. Traditionally, that plot type shows the range of a distribution, including deviations, quartiles, and medians. Visually, however, it provides a perfect model of a stock’s trading performance. We can use it to show the opening and closing prices, as well as the high and low values during the period.

The sparklines library can draw a box plot for us, but normally it calculates the values to display given the distribution as input data. In our case we don’t want to use the standard statistical calculations. Instead, we can use an option that tells the library to use pre-computed values. The library expects at least five values:

For our example, we’ll provide the following values instead:

We’ll also color the median bar red or green depending on whether the stock gained or lost value during the period.

Here’s the code that creates that chart in response to the sparklineRegionChange event. The data for the chart (lines 3-7) is simply the five values extracted from the stock data for the appropriate week. As lines 11 and 12 demonstrate, we can change the color of the median bar depending on whether the stock finished higher or lower for the day.

1
2
3
4
5
6
7
8
9
10
11
12
13
$("#composite-chart4 .details")
    .sparkline([
        stock[idx].low, 
        Math.min(stock[idx].open,stock[idx].close), 
        stock[idx].adj_close, 
        Math.max(stock[idx].open,stock[idx].close), 
        stock[idx].high
    ], {
        type: "box",
        showOutliers: false,
        medianColor: (stock[idx].open < stock[idx].close)
         ? "green" : "red"
    });

When the mouse leaves the chart region, we can remove the box plot by emptying its container.

1
$(".details").empty();

Now as our users mouse over the chart area, they can see a visual representation of the stock’s price range during each period.

 
Interactive annotations can be charts themselves in addition to text.

Responding to Click Events

Throughout this chapter we’ve looked at how to include a lot of visual information in a small space, making it easier to integrate a visualization within a web page. The basic sparkline by itself is very efficient, and previous examples have added annotations and composites to increase the information density further. Sometimes, however, there’s just no way to fit all the possible data in a small enough space. Even in these cases, though, the interactive nature of the web can help us out. Our web page can start with a compact visualization but expand to a different view—one with richer details—with a simple click or tap.

Indeed, the compact quality of sparklines seems to invite interaction. In every usability test I’ve performed that included sparklines on a web page, the participants invariably clicked on the chart. That was true even when there were no other details that the page could provide and the participants had no idea what to expect in response to their clicks. They clicked just to see what happens.

This example continues our stock market example. We’ll begin with the same basic stock price chart we’ve seen before, but enhance it to provide details when users click on the chart region.

Just as in this chapter’s first example, we need to include the sparklines and jQuery libraries in our web page. Because we’re visualizing the same data as in the previous example, we’ll also want to set up the data array exactly as in that example. The HTML markup, however, can be much simpler. All we need is a simple <div> to hold the chart.

1
<div id="stock"></div>

Step 1: Add the Chart

Adding the chart to our markup is easy with the sparklines library. We can use the jQuery .map() function to extract the adjusted close value from our stock array.

1
$('#stock').sparkline($.map(stock, function(wk) { return wk.adj_close; }));

The static chart of figure , which shows the stock performance probably looks familiar by now.

Starting with a static chart ensures the visualization is sound.

Step 2: Handle Click Events

The sparklines library makes it easy for us to handle click events. When users click on a chart region, the library generates a custom sparklineClick event. The event data includes all of the normal click properties, plus information about where on the chart the user clicked. To be notified of clicks, we define a handler for that custom event.

1
2
3
4
5
6
7
$('#stock')
    .sparkline($.map(stock, function(wk) { return wk.adj_close; }))
    .on('sparklineClick', function(ev) {
        var sparkline = ev.sparklines[0],
        region = sparkline.getCurrentRegionFields();
        /* region.x and region.y are the coordinates of the click */
    });

Now that we’re set up to receive sparklineClick events, we can write the code to respond to them. For our example, let’s reveal a detailed financial analysis widget. Many web services, including Yahoo! and Google, have similar widgets, but we’ll use one from TradingView. As is typical, TradingView provides code for the widget as an HTML <iframe>. We can wrap that <iframe> in our own <div> and place it immediately after the chart. We set a display property of none so that the contents are initially hidden. (The following snippet omits the details of the <iframe> element for clarity.)

1
2
<div id="stock"></div>
<div id="widget" style="display:none"><iframe></iframe></div>

Now our event handling code can reveal the widget using the jQuery show() function.

1
2
3
  .on('sparklineClick', function(ev) {
    $("#widget").show();
  });

That works to reveal the details, but as figure shows, the resulting presentation isn’t as elegant as it could be since the details appear so abruptly.

Mouse clicks can reveal more details for a chart.

Step 3: Improve the Transitions

Instead of simply revealing the widget beneath the chart, it would be better to have the widget replace the chart. And if we’re going to do that, we’ll also want to give the users a chance to restore the chart and hide the widget. For that last function, we include a "widget-control" <div> in the following code (lines 2-4) for controlling the widget’s visibility. The only content we need for this controller is a close symbol floated right. Just like the widget itself, the controller is initially hidden.

1
2
3
4
5
6
7
<div id="stock"></div>
<div id="widget-control" style="width:600px;display:none">
    <a href="#" style="float:right">&times;</a>
</div>
<div id="widget" style="width:600px;display:none">
    <iframe></iframe>
</div>

Now when the user clicks on the chart, we reveal the widget, reveal the controller, and hide the chart.

1
2
3
4
5
.on('sparklineClick', function(ev) {
    $("#widget").show();
    $("#widget-control").show();
    $("#stock").hide();
});

Next we intercept clicks on the close symbol in the widget controller. We first prevent default event handling; otherwise, the browser will jump disconcertingly to the top of the page. Then we hide the widget and its controller while revealing the chart again.

1
2
3
4
5
6
$("#widget-control a").click(function(ev) {
    ev.preventDefault();
    $("#widget").hide();
    $("#widget-control").hide();
    $("#stock").show();
})

Finally, we need to give the user some indication that this interaction is possible. On the chart, we can override the sparklines library’s default tooltip in line 4 to let users know that more details are available.

1
2
3
4
5
$('#stock')
    .sparkline(
        $.map(stock, function(wk) { return wk.adj_close; }),
        { tooltipFormatter: function() {return "Click for details"; } }
    );

And for the widget controller, we simply add a title attribute in line 3 to tell users how to hide the widget.

1
2
3
4
5
6
7
<div id="stock"></div>
<div id="widget-control" style="width:600px;display:none">
    <a href="#" title="Click to hide" style="float:right;">&times;</a>
</div>
<div id="widget" style="width:600px;display:none">
    <iframe></iframe>
</div>

These additions give us the simple sparkline chart in figure , which expands to a wealth of details with a single click. The close symbol in the upper right lets users return to the more compact sparkline.

Mouse clicks can reveal more details for a chart.

Step 4: Animation

For the final touch to our visualization, let’s do something about the abrupt hiding and revealing of the visualization components. A smoother animation will help our users follow the transition, and jQuery makes it easy enough to implement. There are lots of animation effects available in the jQuery UI library, but the basic functionality of jQuery’s core is fine for this example. We simply replace the show() and hide() functions with slideDown() and slideUp() respectively.

1
2
3
4
5
6
7
8
9
10
11
.on('sparklineClick', function(ev) {
    $("#widget").slideDown();
    $("#widget-control").slideDown();
    $("#stock").slideUp();
});
$("#widget-control a").click(function(ev) {
    ev.preventDefault();
    $("#widget").slideUp();
    $("#widget-control").slideUp();
    $("#stock").slideDown();
})

At this point we can call our visualization complete; the final product is shown in figure . The compact sparkline smoothly transitions to reveal detailed information when the user clicks, and those details transition back to the sparkline when the user closes them.

Animating transitions can make the visualization less jarring to users.

Updating Charts in Real Time

As we’ve seen in this chapter’s other examples, sparklines are great for integrating visualizations in a complete web page. They can be embedded in text content or used as table elements. Another application that suits sparklines well is an information dashboard. Effective dashboards summarize the health of the underlying system at a glance. When users don’t have the time to read through pages of texts or detailed graphics, the information density of sparklines makes them an ideal tool.

In addition to high information density, most dashboards have another requirement: they must be up-to-date. For web-based dashboards, that means the contents should be continuously updated, even while users are viewing the page. There is no excuse for requiring users to refresh their browsers. Fortunately, the sparklines library makes it easy to accommodate this requirement as well.

Just as in this chapter’s first example, we need to include the sparklines and jQuery libraries in our web page. For this visualization we’ll show both a chart and the most recent value of the data. We define <div> elements for each and place both in a containing <div>. The following code includes some styles inline, but you could place them in an external stylesheet. Here the styles are just meant to position the value immediately to the right of the chart rather than on a separate line.

1
2
3
4
<div id="dashboard">
    <div id="chart" style="float:left"></div>
    <div id="value" style="float:left"></div>
</div>

Step 1: Retrieve the Data

In a real dashboard example, the server would provide the data to display and updates to that data. As long as the frequency of the updates was modest (not faster than once every five seconds or so), we could simply poll the server for updates on a regular interval. It’s probably not a good idea, however, to use the JavaScript setInterval() function to control the polling interval. That may seem strange at first because setInterval() executes a function periodically, which would seem to meet the requirements exactly. The situation is not quite that simple, however. If the server or network encounters a problem, then requests triggered by setInterval()will continue unabated, stacking up in a queue. Then, when communication with the server is restored, all of the pending requests will immediately finish, and we’d have a flood of data to handle.

To avoid this problem, we can use the setTimeout() function instead. That function executes only once, so we’ll have to keep calling it explicitly. By doing that, though, we can make sure that we send a request to the server only after the current one finishes. This approach avoids stacking up a queue of requests.

1
2
3
4
5
6
7
8
9
10
11
12
(function getData(){
    setTimeout(function(){
        // request the data from the server
        $.ajax({ url: "/api/data", success: function(data) {

            // data has the response from the server

            // now prepare to ask for updated data
            getData();
        }, dataType: "json"});
    }, 30000);  // 30000: wait 30 seconds to make the request
})();

Notice that the structure of the code defines the getData() function and immediately executes it. The closing pair of parentheses at the end of line 12 triggers the immediate execution.

Within the success callback we set up a recursive call to getData() in line 9 so the function executes again whenever the server responds with data.

Step 2: Update the Visualization

Whenever we receive updated information from the server, we can simply update the chart and value. The code needs only a straightforward call to the sparklines library and a jQuery function to update the value. We’ve added that to the code here in lines 6 and 7 below.

1
2
3
4
5
6
7
8
9
10
11
12
(function getData(){
    setTimeout(function(){
        // request the data from the server
        $.ajax({ url: "/api/data", success: function(data) {

            $('#chart').sparkline(data);
            $('#value').text(data.slice(-1));

            getData();
        }, dataType: "json"});
    }, 30000);  // 30000: wait 30 seconds to make the request
})();

Figure shows what a default chart looks like. Of course you can specify both the chart and text styles as appropriate for your own dashboard.

A live updating chart can show real time-data.

Summing Up

In this chapter, we’ve considered various techniques for integrating visualizations within a web page. We’ve seen that sparklines are an excellent tool. Because they provide a lot of visual information in a small space, they leave room for other elements of the page, including text blocks, tables, and dashboards. We’ve considered several ways to increase the information density even further with annotations, composite charts, and click events. Finally, we looked at how to create charts that update in real time, accurately visualizing the up-to-the-minute status of an underlying system.

Continue reading: Chapter 4: Creating Specialized Graphs.