While data visualization is growing as a medium on the Open Web, practitioners of the field still struggle to make data visualization “work” on different screens. The question “how do I render at different sizes?” is an important one, but only tackles a portion of the greater challenge of “what does it mean to create data visualization on a smaller screen?” Understanding how to best target a mobile audience (possibly on the go) and how to do the data & narrative justice when rendering complex graphics, is a fascinating question that we hope to explore in the coming months here in Bocoup’s Data Vis Central.
Having said that, there’s no time like the present. Following amazing work that we’ve seen our colleagues create (like Gabriel Florit’s superb work at the Boston Globe) we realized that one of the key requirements for supporting different screen sizes is to render slightly (or completely) different views.
Introducing modes
To accomplish this we added a new concept to our d3.chart.base: modes
. Conceptually, a mode
is a way to describe the environment that the chart is in. In practice, mode
is simply a combination of a name (such as “mobile”) and a function that can test to see whether a chart is in this mode or not. To demonstrate this functionality, we could define screen-size based modes such as “web”, “tablet” and “mobile” and utlize media queries and the chart width to determine the correct mode, like so:
01.mobile.test.js
modes: {
mobile : function() {
// if the chart is less than 480 pixels wide or the
// screen is less than 480px wide
return this.width() <= 480 ||
Modernizr.mq("only all and (max-width: 480px)");
},
}
In this example we’ve used Modernizr, a JavaScript library that detects HTML5 and CSS3 features in the user’s browser. Using Modernizr’s media query test, we can check the width of our screen as part of our mode test.
Why modes
?
This approach allows us to define different breakpoints for various viewport sizes, but doesn’t limit us to screen sizes alone. Practically, nothing precludes the modes from being used for other purposes. For example, one might use modes to determine the presence of browser features like CSS3 or WebGL and offer different rendering based on available functionality. To go further, one can try to determine the available processing power to choose the appropriate number of nodes in a scatterplot, for example.
Basic Example
Consider the following basic example. I am rendering rectangles along the X axis such that they are positioned according to their values. At full “web” screen sizes I just want to render those boxes. At smaller “tablet” sizes, I still want to render them, but make them smaller. On a “mobile” device, I actually want to eliminate them altogether and just render some text describing the data. Ideally, I wouldn’t have to write three different charts to accomplish this goal.
You can run the following example here (you might want to open it up and resize your browser window a few times):
This example also comes in d3.chart.base’s example
folder.
d3.chart.base is a very small chart based on the Miso Project’s d3.chart, intended to provide some basic functionality that other charts may want to inherit from.
Defining modes
When creating a chart that extends the BaseChart
defined in d3.chart.base
, one can define
as many modes as one desires that test various configuration the chart might encounter, like so:
The relationship between layers
and modes
Once modes are defined, creating layers
that are only visible in certain modes is incredibly easy.
It requires specifying which of the available modes should “activate” this layer. When not specified,
all modes will show the layer in question. A layer will be visible when activated and its lifecycle events
will fire, otherwise it will be hidden from view.
As one might guess from its name, the chart’s mode
method returns the current mode. This gives chart authors more granular control over the behavior of their chart in the context of modes like so:
Note that the .mode()
function is not a setter, as the mode cannot be overriden manually.
When do modes
update?
The mode functions are triggered when the viewport dimensions change (i.e. when the screen resolution updates or device orientation changes.) We would love to hear about other events in which the mode checking should happen.
We’d love your feedback on this approach as we continue to explore how one might venture into the realm of responsive data visualization on the Open Web!
Big thanks to Mike Pennisi and Yannick Assogba who have contributed code and brain power to this question.
Comments
We moved off of Disqus for data privacy and consent concerns, and are currently searching for a new commenting tool.
Nice work. I guess the key is re-usability: As long as graph objects have this knowledge built in, users will effortlessly get the best render available.
Absolutely. If you extend a chart that already has modes defined, it will also receive those modes by default.
I believe this approach is good. One question though: what do you mean by \”activating\” a layer? Does that mean the layer is created, added to the SVG, and then hidden, or not created at all?
Great question. Activating and deactivating means a few things:
* An active layer will be visible, a deactivated layer will be hidden (as in, the display of its base will be set to none.)
* An active layer will respond to lifecycle events and run the code associated with each one of those (if defined), a deactivated layer won’t.
When a chart is initialized and put in a certain mode, layers that shouldn’t be drawn in that mode are not actually created. They are stored in case they do need to be created on a mode change.
Great work irene!
I’ve been working with d3 over the past year and let me tell you this is probably the best way to get around responsiveness and yet retain the full power of D3, there are tons of libraries out there that leverage on top of D3, but this is something else.
Yet I feel the library still needs a lot of work, I dont like the way it handles mode changes, It should cleaner than an if then else block.
Yet the full power of this base constructor is yet to be discovered !