Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Some clarification on reusable charts

Tags:

d3.js

In 'Towards Reusable Charts', Mike lays out an example time-series chart at the end. In it, he has this line:

// Select the svg element, if it exists.
var svg = d3.select(this).selectAll("svg").data([data]);

This is happening within the definition of a reusable chart object. I am having a hard time grasping this statement.

  1. First, why use select(this)? Is the this context the current instance of the chart object? If so, why bother, why not just go ahead and select the svg elements on the page?

  2. Why the selectAll? Doesn't each chart object correspond to one chart?

  3. Why [data], instead of just data, like in most other examples I've seen? Further, why are we binding the data to the svg element, rather than to (say) the path elements?

I'd be grateful for any help.

like image 988
Sam Selikoff Avatar asked Feb 02 '13 19:02

Sam Selikoff


1 Answers

A reusable chart, as defined in the article, can be used for inserting the chart to any DOM element, possibly multiple times on the same page, albeit with different data. Moreover, owing to the statement you have pointed out, it can be used to update a graph which already exists within a DOM element. The way it would be used is (from the article):

var formatDate = d3.time.format("%b %Y");

var chart = timeSeriesChart()
             .x(function(d) { return formatDate.parse(d.date); })
             .y(function(d) { return +d.price; });

// Put the chart _inside_ #example1
d3.select("#example1")
  .datum(data1)
  .call(chart);

// Put the same chart _inside_ #example2
d3.select("#example2")
  .datum(data1)
  .call(chart);

// Update the data for #example1, without appending another <svg> element
setInterval(function () {
    d3.select("#example1")
      .datum(data2)
      .call(chart);
}, 5000);

Now to address your concerns of how it does that:

  1. First note that this code is run inside selection.each(...). As per the API reference, the this context inside .each is the DOM element. The API reference further goes to say:

    The each operator can be used to process selections recursively, by using d3.select(this) within the callback function.

    So d3.select(this) just returns a d3.selection containing only the current DOM element, which may or may not contain an <svg> element inside it. Selecting a/every <svg> element on the page will not result in a very reusable chart since it'll mess with the complete page instead of only the DOM element you want the chart to appear in.

  2. Using selectAll creates a new grouping of elements for D3 to work on, opposed to select which preserves the original grouping. See this article for more details on how nested selections work. And, yes, you are right that the selectAll group here will be used to create or update only one <svg> element, which brings us to the next point.

  3. One of the underlying concepts behind D3 is data-joins, where an array of data is joined to an group of DOM elements in a one-to-one fashion. In this case, we are interested in appending/updating some data to exactly one <svg> element. Using [data] provides us with a one-element array which can be bound to one <svg> element. Alternatively, this could have been written as:

  var svg = d3.select(this).selectAll("svg").data([1]); // No `data`

   // Update the area path.
   g.select(".area")
      .data(data)    // Specifying data explicitly
      .attr("d", area.y0(yScale.range()[0]));

   // Update the line path.
   g.select(".line")
      .data(data)   // Specifying data explicitly
      .attr("d", line);

However, it is more pleasing to have data already available to the sub-selections by binding it to the <svg> element.

like image 163
musically_ut Avatar answered Sep 22 '22 03:09

musically_ut