epubjs
Version:
Render ePub documents in the browser, across many devices
263 lines (260 loc) • 165 kB
HTML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:pls="http://www.w3.org/2005/01/pronunciation-lexicon" xmlns:ssml="http://www.w3.org/2001/10/synthesis" xmlns:svg="http://www.w3.org/2000/svg"><head><title>Chapter 9. Updates, Transitions, and Motion</title><link rel="stylesheet" type="text/css" href="core.css"/><meta name="generator" content="DocBook XSL Stylesheets V1.76.1"/><link rel="up" href="index.html" title="Interactive Data Visualization for the Web"/><link rel="prev" href="ch08.html" title="Chapter 8. Axes"/><link rel="next" href="ch10.html" title="Chapter 10. Interactivity"/></head><body><section class="chapter" title="Chapter 9. Updates, Transitions, and Motion" epub:type="chapter" id="updates-chapter9"><div class="titlepage"><div><div><h2 class="title">Chapter 9. Updates, Transitions, and Motion</h2></div></div></div><p>Until this point, we have used only static datasets. But real-world
data almost always <span class="emphasis"><em>changes</em></span> over time. And you might want your
visualization to reflect those changes.<a id="ix_updates" class="indexterm"/></p><p>In D3 terms, those changes are handled by <span class="emphasis"><em>updates</em></span>. The visual
adjustments are made pretty with <span class="emphasis"><em>transitions</em></span>, which can employ
<span class="emphasis"><em>motion</em></span> for perceptual benefit.<a id="I_indexterm9_id306629" class="indexterm"/></p><p>We’ll start by generating a visualization with one dataset, and then
changing the data completely.</p><div class="sect1" title="Modernizing the Bar Chart"><div class="titlepage"><div><div><h2 class="title" style="clear: both" id="_modernizing_the_bar_chart">Modernizing the Bar Chart</h2></div></div></div><p>Let’s revisit our trusty old bar chart in <a class="xref" href="ch09.html#The_bar_chart_as_seen_last" title="Figure 9-1. The bar chart, as seen last">Figure 9-1</a>.</p><div class="figure"><a id="The_bar_chart_as_seen_last"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject9_id306680"/><img src="httpatomoreillycomsourceoreillyimages1614805.png" alt="The bar chart, as seen last"/></div></div><div class="figure-title">Figure 9-1. The bar chart, as seen last</div></div><p>If you examine the code in <span class="emphasis"><em>01_bar_chart.html</em></span>, you’ll see that we used
this static dataset:</p><a id="I_programlisting9_id306706"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">dataset</code> <code class="o">=</code> <code class="p">[</code> <code class="mi">5</code><code class="p">,</code> <code class="mi">10</code><code class="p">,</code> <code class="mi">13</code><code class="p">,</code> <code class="mi">19</code><code class="p">,</code> <code class="mi">21</code><code class="p">,</code> <code class="mi">25</code><code class="p">,</code> <code class="mi">22</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">15</code><code class="p">,</code> <code class="mi">13</code><code class="p">,</code>
<code class="mi">11</code><code class="p">,</code> <code class="mi">12</code><code class="p">,</code> <code class="mi">15</code><code class="p">,</code> <code class="mi">20</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">17</code><code class="p">,</code> <code class="mi">16</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">23</code><code class="p">,</code> <code class="mi">25</code> <code class="p">];</code></pre><p>Since then, we’ve learned how to write more flexible code, so our chart
elements resize to accommodate different sized datasets (meaning
shorter or longer arrays) and different data values (smaller or larger
numbers). We accomplished that flexibility using D3 scales, so I’d like
to start by bringing our bar chart up to speed.<a id="ix_ubcht" class="indexterm"/></p><p>Ready? Okay, just give me a sec…</p><p>Aaaaaand, done! Thanks for waiting.</p><p><a class="xref" href="ch09.html#A_scalable_flexible_bar_chart" title="Figure 9-2. A scalable, flexible bar chart">Figure 9-2</a> looks pretty similar, but a lot has changed under the hood. You can<a id="ix_bcscal" class="indexterm"/>
follow along by opening up <span class="emphasis"><em>02_bar_chart_with_scales.html</em></span>.</p><div class="figure"><a id="A_scalable_flexible_bar_chart"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject9_id306774"/><img src="httpatomoreillycomsourceoreillyimages1614806.png" alt="A scalable, flexible bar chart"/></div></div><div class="figure-title">Figure 9-2. A scalable, flexible bar chart</div></div><p>To start, I adjusted the width and height, to make the chart taller and
wider:</p><a id="I_programlisting9_id306795"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">w</code> <code class="o">=</code> <code class="mi">600</code><code class="p">;</code>
<code class="kd">var</code> <code class="nx">h</code> <code class="o">=</code> <code class="mi">250</code><code class="p">;</code></pre><p>Next, I introduced an <span class="emphasis"><em>ordinal scale</em></span> to handle the left/right<a id="I_indexterm9_id306807" class="indexterm"/>
positioning of bars and labels along the x-axis:</p><a id="I_programlisting9_id306817"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">xScale</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">scale</code><code class="p">.</code><code class="nx">ordinal</code><code class="p">()</code>
<code class="p">.</code><code class="nx">domain</code><code class="p">(</code><code class="nx">d3</code><code class="p">.</code><code class="nx">range</code><code class="p">(</code><code class="nx">dataset</code><code class="p">.</code><code class="nx">length</code><code class="p">))</code>
<code class="p">.</code><code class="nx">rangeRoundBands</code><code class="p">([</code><code class="mi">0</code><code class="p">,</code> <code class="nx">w</code><code class="p">],</code> <code class="mf">0.05</code><code class="p">);</code></pre><p>This may seem like gobbledegook, so I’ll walk through it one line at a
time.</p><div class="sect2" title="Ordinal Scales, Explained"><div class="titlepage"><div><div><h3 class="title" id="_ordinal_scales_explained">Ordinal Scales, Explained</h3></div></div></div><p>First, in this line:</p><a id="I_programlisting9_id306845"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">xScale</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">scale</code><code class="p">.</code><code class="nx">ordinal</code><code class="p">()</code></pre><p>we declare a new variable called <code class="literal">xScale</code>, just as we had done with our
scatterplot. Only here, instead of a <span class="emphasis"><em>linear</em></span> scale, we create an
<span class="emphasis"><em>ordinal</em></span> one. Ordinal scales are typically used for ordinal data,
typically categories with some inherent <span class="emphasis"><em>order</em></span> to them, such as:</p><div class="itemizedlist"><ul class="itemizedlist"><li class="listitem">
freshman, sophomore, junior, senior
</li><li class="listitem">
grade B, grade A, grade AA
</li><li class="listitem">
strongly dislike, dislike, neutral, like, strongly like
</li></ul></div><p>We don’t have true ordinal data for use with this bar chart. Instead, we
just want the bars to be drawn from left to right using the same order
in which values occur in our dataset. D3’s ordinal scale is useful in
this situation, when we have many visual elements (vertical bars) that
are positioned in an arbitrary order (left to right), but must be evenly
spaced. This will become clear in a moment.<a id="I_indexterm9_id306905" class="indexterm"/></p><a id="I_programlisting9_id306913"/><pre class="programlisting"><code class="p">.</code><code class="nx">domain</code><code class="p">(</code><code class="nx">d3</code><code class="p">.</code><code class="nx">range</code><code class="p">(</code><code class="nx">dataset</code><code class="p">.</code><code class="nx">length</code><code class="p">))</code></pre><p>This next line of code sets the input domain for the scale. Remember how
linear scales need a two-value array to set their domains, as in
<code class="literal">[0, 100]</code>? For a linear scale, that array would set the low and high
values of the domain. But ordinal domains are, well, ordinal, so they
don’t think in linear, quantitative terms. To set the domain of an
ordinal scale, you typically specify an array with the category names,
as in:</p><a id="I_programlisting9_id306929"/><pre class="programlisting"><code class="p">.</code><code class="nx">domain</code><code class="p">([</code><code class="s2">"freshman"</code><code class="p">,</code> <code class="s2">"sophomore"</code><code class="p">,</code> <code class="s2">"junior"</code><code class="p">,</code> <code class="s2">"senior"</code><code class="p">])</code></pre><p>For our bar chart, we don’t have explicit categories, but we could
assign each data point or bar an ID value corresponding to its position
within the <code class="literal">dataset</code> array, as in 0, 1, 2, 3, and so on. So perhaps our
domain statement could read:</p><a id="I_programlisting9_id306944"/><pre class="programlisting"><code class="p">.</code><code class="nx">domain</code><code class="p">([</code><code class="mi">0</code><code class="p">,</code> <code class="mi">1</code><code class="p">,</code> <code class="mi">2</code><code class="p">,</code> <code class="mi">3</code><code class="p">,</code> <code class="mi">4</code><code class="p">,</code> <code class="mi">5</code><code class="p">,</code> <code class="mi">6</code><code class="p">,</code> <code class="mi">7</code><code class="p">,</code> <code class="mi">8</code><code class="p">,</code> <code class="mi">9</code><code class="p">,</code>
<code class="mi">10</code><code class="p">,</code> <code class="mi">11</code><code class="p">,</code> <code class="mi">12</code><code class="p">,</code> <code class="mi">13</code><code class="p">,</code> <code class="mi">14</code><code class="p">,</code> <code class="mi">15</code><code class="p">,</code> <code class="mi">16</code><code class="p">,</code> <code class="mi">17</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">19</code><code class="p">])</code></pre><p>It turns out there is a very simple way to quickly generate an array of
sequential numbers: the <code class="literal">d3.range()</code> method.<a id="I_indexterm9_id306957" class="indexterm"/><a id="I_indexterm9_id306965" class="indexterm"/></p><p>While viewing <span class="emphasis"><em>02_bar_chart_with_scales.html</em></span>, go ahead and open up the
console, and type the following:</p><pre class="screen">d3.range(10)</pre><p>You should see the following output array:</p><pre class="screen">[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]</pre><p>How nice is that? D3 saves you time once again (and the hassle of extra
<code class="literal">for()</code> loops).</p><p>Coming back to our code, it should now be clear what’s happening here:</p><a id="I_programlisting9_id307002"/><pre class="programlisting"><code class="p">.</code><code class="nx">domain</code><code class="p">(</code><code class="nx">d3</code><code class="p">.</code><code class="nx">range</code><code class="p">(</code><code class="nx">dataset</code><code class="p">.</code><code class="nx">length</code><code class="p">))</code></pre><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">
<code class="literal">dataset.length</code>, in this case, is evaluted as <code class="literal">20</code>, because we have
20 items in our dataset.
</li><li class="listitem">
<code class="literal">d3.range(20)</code> is then evaluated, which returns this array:
<code class="literal">[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]</code>.
</li><li class="listitem">
Finally, <code class="literal">domain()</code> sets the domain of our new ordinal scale to
those values.
</li></ol></div><p>This might be somewhat confusing because we are using numbers (0, 1, 2…)
as ordinal values, but ordinal values are typically nonnumeric.</p></div><div class="sect2" title="Round Bands Are All the Range These Days"><div class="titlepage"><div><div><h3 class="title" id="_round_bands_are_all_the_range_these_days">Round Bands Are All the Range These Days</h3></div></div></div><p>The great thing about <code class="literal">d3.scale.ordinal()</code> is it supports <span class="emphasis"><em>range
banding</em></span>. Instead of returning a continuous range, as any quantitative
scale (like <code class="literal">d3.scale.linear()</code>) would, ordinal scales use <span class="emphasis"><em>discrete</em></span>
ranges, meaning the output values are determined in advance, and could
be numeric or not.<a id="I_indexterm9_id307091" class="indexterm"/><a id="I_indexterm9_id307097" class="indexterm"/><a id="I_indexterm9_id307104" class="indexterm"/></p><p>We could use <code class="literal">range()</code> like everyone else, <span class="emphasis"><em>or</em></span> we can be smooth and use
<code class="literal">rangeBands()</code>, which takes a low and high value, and automatically
divides it into even chunks or “bands,” based on the length of the
domain. For example:</p><a id="I_programlisting9_id307125"/><pre class="programlisting"><code class="p">.</code><code class="nx">rangeBands</code><code class="p">([</code><code class="mi">0</code><code class="p">,</code> <code class="nx">w</code><code class="p">])</code></pre><p>this says “calculate even bands starting at 0 and ending at <code class="literal">w</code>, then set
this scale’s range to those bands.” In our case, we specified 20 values
in the domain, so D3 will calculate:</p><pre class="screen">(w - 0) / xScale.domain().length
(600 - 0) / 20
600 / 20
30</pre><p>In the end, each band will be <code class="literal">30</code> “wide.”</p><p>It is also possible to include a second parameter, which includes a bit
of spacing between each band. Here, I’ve used <code class="literal">0.2</code>, meaning that 20 percent of
the width of each band will be used for spacing in between bands:</p><a id="I_programlisting9_id307161"/><pre class="programlisting"><code class="p">.</code><code class="nx">rangeBands</code><code class="p">([</code><code class="mi">0</code><code class="p">,</code> <code class="nx">w</code><code class="p">],</code> <code class="mf">0.2</code><code class="p">)</code></pre><p>We can be even smoother and use <code class="literal">rangeRoundBands()</code>, which is the same
as <code class="literal">rangeBands()</code>, except that the output values are rounded to the
nearest whole pixel, so 12.3456 becomes just 12, for example. This is
helpful for keeping visual elements lined up precisely on the pixel
grid, for clean, sharp edges.<a id="I_indexterm9_id307179" class="indexterm"/><a id="I_indexterm9_id307188" class="indexterm"/><a id="I_indexterm9_id307194" class="indexterm"/></p><p>I’ll also decrease the amount of spacing to just 5 percent. So, our final line of code in that statement is:</p><a id="I_programlisting9_id307209"/><pre class="programlisting"><code class="p">.</code><code class="nx">rangeRoundBands</code><code class="p">([</code><code class="mi">0</code><code class="p">,</code> <code class="nx">w</code><code class="p">],</code> <code class="mf">0.05</code><code class="p">);</code></pre><p>This gives us nice, clean pixel values, with a teensy bit of visual
space between them.</p></div><div class="sect2" title="Referencing the Ordinal Scale"><div class="titlepage"><div><div><h3 class="title" id="_referencing_the_ordinal_scale">Referencing the Ordinal Scale</h3></div></div></div><p>Later in the code (and I recommend viewing the source), when we create<a id="I_indexterm9_id307232" class="indexterm"/>
the <code class="literal">rect</code> elements, we set their horizontal, x-axis positions like so:</p><a id="I_programlisting9_id307245"/><pre class="programlisting"><code class="c1">//Create bars</code>
<code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">)</code>
<code class="p">.</code><code class="nx">enter</code><code class="p">()</code>
<code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"x"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">,</code> <code class="nx">i</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">xScale</code><code class="p">(</code><code class="nx">i</code><code class="p">);</code> <code class="c1">// <-- Set x values</code>
<code class="p">})</code>
<code class="err">…</code></pre><p>Note that, because we include <code class="literal">d</code> and <code class="literal">i</code> as parameters to the anonymous
function, D3 will automatically pass in the correct values. Of course,
<code class="literal">d</code> is the current datum, and <code class="literal">i</code> is its index value. So <code class="literal">i</code> will be
passed 0, 1, 2, 3, and so on.</p><p>Coincidentally (hmmm!), we used those same values (0, 1, 2, 3…) for our
ordinal scale’s input domain. So when we call <code class="literal">xScale(i)</code>, <code class="literal">xScale()</code>
will look up the ordinal value <code class="literal">i</code> and return its associated output
(band) value. (You can verify all this for yourself in the console. Just
try typing <code class="literal">xScale(0)</code> or <code class="literal">xScale(5)</code>.)</p><p>Even better, setting the widths of these bars just got a lot easier.
Before using the ordinal scale, we had:</p><a id="I_programlisting9_id307306"/><pre class="programlisting"><code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"width"</code><code class="p">,</code> <code class="nx">w</code> <code class="o">/</code> <code class="nx">dataset</code><code class="p">.</code><code class="nx">length</code> <code class="o">-</code> <code class="nx">barPadding</code><code class="p">)</code></pre><p>We don’t even need <code class="literal">barPadding</code> anymore because now the padding is built
into <code class="literal">rangeRoundBands()</code>. Setting the width of each <code class="literal">rect</code> needs only<a id="I_indexterm9_id307329" class="indexterm"/><a id="I_indexterm9_id307336" class="indexterm"/>
this:</p><a id="I_programlisting9_id307344"/><pre class="programlisting"> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"width"</code><code class="p">,</code> <code class="nx">xScale</code><code class="p">.</code><code class="nx">rangeBand</code><code class="p">())</code></pre><p>Isn’t it nice when D3 does the math for you?</p></div><div class="sect2" title="Other Updates"><div class="titlepage"><div><div><h3 class="title" id="_other_updates">Other Updates</h3></div></div></div><p>I’ve made several other updates in <span class="emphasis"><em>02_bar_chart_with_scales.html</em></span>,
including creating a new linear scale <code class="literal">yScale</code> to calculate vertical
values. You already know how to use linear scales, so you can skim the
source and note how that’s being used to set the bar heights.<a id="I_indexterm9_id307373" class="indexterm"/></p></div></div><div class="sect1" title="Updating Data"><div class="titlepage"><div><div><h2 class="title" style="clear: both" id="_updating_data">Updating Data</h2></div></div></div><p>Okay, once again, we have the amazing bar chart shown in <a class="xref" href="ch09.html#The_bar_chart" title="Figure 9-3. The bar chart">Figure 9-3</a>, flexible enough to
handle any data we can throw at it.<a id="I_indexterm9_id307400" class="indexterm"/></p><div class="figure"><a id="The_bar_chart"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject9_id307417"/><img src="httpatomoreillycomsourceoreillyimages1614807.png" alt="The bar chart"/></div></div><div class="figure-title">Figure 9-3. The bar chart</div></div><p>Or is it? Let’s see.</p><p>The simplest kind of update is when all data values are updated at the
same time <span class="emphasis"><em>and</em></span> the number of values stays the same.</p><p>The basic approach in this scenario is this:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">
Modify the values in your dataset.
</li><li class="listitem">
Rebind the new values to the existing elements (thereby overwriting
the original values).
</li><li class="listitem">
Set new attribute values as needed to update the visual display.
</li></ol></div><p>Before any of those steps can happen, though, some <span class="emphasis"><em>event</em></span> needs to kick
things off. So far, all of our code has executed immediately on page
load. We <span class="emphasis"><em>could</em></span> have our update run right after the initial drawing
code, but it would happen imperceptibly fast. To make sure we can
observe the change as it happens, we will separate our update code from
everything else. We will need a “trigger,” something that happens
<span class="emphasis"><em>after</em></span> page load to apply the updates. How about a mouse click?</p><div class="sect2" title="Interaction via Event Listeners"><div class="titlepage"><div><div><h3 class="title" id="_interaction_via_event_listeners">Interaction via Event Listeners</h3></div></div></div><p>Any DOM element can be used, so rather than design a fancy button, I’ll<a id="I_indexterm9_id307513" class="indexterm"/><a id="I_indexterm9_id307522" class="indexterm"/>
add a simple <code class="literal">p</code> paragraph to the HTML’s <code class="literal">body</code>:</p><a id="I_programlisting9_id307538"/><pre class="programlisting"><code class="nt"><p></code>Click on this text to update the chart with new data values (once).<code class="nt"></p></code></pre><p>Then, down at the end of our D3 code, let’s add the following:</p><a id="I_programlisting9_id307547"/><pre class="programlisting"><code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"p"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">on</code><code class="p">(</code><code class="s2">"click"</code><code class="p">,</code> <code class="kd">function</code><code class="p">()</code> <code class="p">{</code>
<code class="c1">//Do something on click</code>
<code class="p">});</code></pre><p>This selects our new <code class="literal">p</code>, and then adds an <span class="emphasis"><em>event listener</em></span> to that
element. Huh?</p><p>In JavaScript, events are happening all the time. Not exciting events,
like huge parties, but really insignificant events like <code class="literal">mouseover</code> and
<code class="literal">click</code>. Most of the time, these insignificant events go ignored (just
as in life, perhaps). But if someone is <span class="emphasis"><em>listening</em></span>, then the event will
be <span class="emphasis"><em>heard</em></span>, and can go down in posterity, or at least trigger some sort
of DOM interaction. (Rough JavaScript parallel of classic koan: if an
event occurs, and no listener hears it, did it ever happen at all?)<a id="I_indexterm9_id307588" class="indexterm"/><a id="I_indexterm9_id307597" class="indexterm"/></p><p>An event <span class="emphasis"><em>listener</em></span> is an anonymous function that <span class="emphasis"><em>listens</em></span> for a<a id="I_indexterm9_id307616" class="indexterm"/><a id="I_indexterm9_id307623" class="indexterm"/><a id="I_indexterm9_id307632" class="indexterm"/>
specific event <span class="emphasis"><em>on</em></span> a specific element or elements. D3’s method
<code class="literal">selection.on()</code> provides a nice shorthand for adding event listeners.
As you can see, <code class="literal">on()</code> takes two arguments: the event <span class="emphasis"><em>type</em></span> (<code class="literal">"click"</code>)
and the listener itself (the anonymous function).<a id="I_indexterm9_id307661" class="indexterm"/></p><p>In this case, the listener listens for a <code class="literal">click</code> event occurring on our<a id="I_indexterm9_id307674" class="indexterm"/>
selection <code class="literal">p</code>. When that happens, the listener function is executed. You
can put whatever code you want in between the brackets of the anonymous
function:</p><a id="I_programlisting9_id307688"/><pre class="programlisting"><code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"p"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">on</code><code class="p">(</code><code class="s2">"click"</code><code class="p">,</code> <code class="kd">function</code><code class="p">()</code> <code class="p">{</code>
<code class="c1">//Do something mundane and annoying on click</code>
<code class="nx">alert</code><code class="p">(</code><code class="s2">"Hey, don't click that!"</code><code class="p">);</code>
<code class="p">});</code></pre><p>We’ll talk a lot more about interactivity in <a class="xref" href="ch10.html" title="Chapter 10. Interactivity">Chapter 10</a>.</p></div><div class="sect2" title="Changing the Data"><div class="titlepage"><div><div><h3 class="title" id="_changing_the_data">Changing the Data</h3></div></div></div><p>Instead of generating annoying pop-ups, I’d rather simply update<a id="I_indexterm9_id307715" class="indexterm"/>
<code class="literal">dataset</code> by overwriting its original values. This is step 1, from
earlier:</p><a id="I_programlisting9_id307730"/><pre class="programlisting"><code class="nx">dataset</code> <code class="o">=</code> <code class="p">[</code> <code class="mi">11</code><code class="p">,</code> <code class="mi">12</code><code class="p">,</code> <code class="mi">15</code><code class="p">,</code> <code class="mi">20</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">17</code><code class="p">,</code> <code class="mi">16</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">23</code><code class="p">,</code> <code class="mi">25</code><code class="p">,</code>
<code class="mi">5</code><code class="p">,</code> <code class="mi">10</code><code class="p">,</code> <code class="mi">13</code><code class="p">,</code> <code class="mi">19</code><code class="p">,</code> <code class="mi">21</code><code class="p">,</code> <code class="mi">25</code><code class="p">,</code> <code class="mi">22</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">15</code><code class="p">,</code> <code class="mi">13</code> <code class="p">];</code></pre><p>Step 2 is to rebind the new values to the existing elements. We can do
that by selecting those <code class="literal">rect</code>s and simply calling <code class="literal">data()</code> one more
time:</p><a id="I_programlisting9_id307749"/><pre class="programlisting"><code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">);</code> <code class="c1">//New data successfully bound, sir!</code></pre></div><div class="sect2" title="Updating the Visuals"><div class="titlepage"><div><div><h3 class="title" id="_updating_the_visuals">Updating the Visuals</h3></div></div></div><p>Finally, step 3 is to update the visual attributes, referencing the
(now-updated) data values. This is super easy, as we simply copy and
paste the relevant code that we’ve already written. In this case, the
<code class="literal">rect</code>s can maintain their horizontal positions and widths; all we
really need to update are their <code class="literal">height</code>s and <code class="literal">y</code> positions. I’ve added
those lines here:</p><a id="I_programlisting9_id307784"/><pre class="programlisting"><code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"y"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">h</code> <code class="o">-</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"height"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">});</code></pre><p>Notice this looks almost exactly like the code that generates the
<code class="literal">rect</code>s initially, only without <code class="literal">enter()</code> and <code class="literal">append()</code>.</p><p>Putting it all together, here is all of our update code in one place:</p><a id="I_programlisting9_id307810"/><pre class="programlisting"><code class="c1">//On click, update with new data</code>
<code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"p"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">on</code><code class="p">(</code><code class="s2">"click"</code><code class="p">,</code> <code class="kd">function</code><code class="p">()</code> <code class="p">{</code>
<code class="c1">//New values for dataset</code>
<code class="nx">dataset</code> <code class="o">=</code> <code class="p">[</code> <code class="mi">11</code><code class="p">,</code> <code class="mi">12</code><code class="p">,</code> <code class="mi">15</code><code class="p">,</code> <code class="mi">20</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">17</code><code class="p">,</code> <code class="mi">16</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">23</code><code class="p">,</code> <code class="mi">25</code><code class="p">,</code>
<code class="mi">5</code><code class="p">,</code> <code class="mi">10</code><code class="p">,</code> <code class="mi">13</code><code class="p">,</code> <code class="mi">19</code><code class="p">,</code> <code class="mi">21</code><code class="p">,</code> <code class="mi">25</code><code class="p">,</code> <code class="mi">22</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">15</code><code class="p">,</code> <code class="mi">13</code> <code class="p">];</code>
<code class="c1">//Update all rects</code>
<code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"y"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">h</code> <code class="o">-</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"height"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">});</code>
<code class="p">});</code></pre><p>Check out the revised bar chart in <span class="emphasis"><em>03_updates_all_data.html</em></span>. It looks like <a class="xref" href="ch09.html#Updateable_bar_chart" title="Figure 9-4. Updatable bar chart">Figure 9-4</a> to start.</p><div class="figure"><a id="Updateable_bar_chart"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject9_id307840"/><img src="httpatomoreillycomsourceoreillyimages1614808.png" alt="Updatable bar chart"/></div></div><div class="figure-title">Figure 9-4. Updatable bar chart</div></div><p>Then click anywhere on the paragraph, and it turns into <a class="xref" href="ch09.html#Bar_chart_data_updated" title="Figure 9-5. Bar chart, data updated">Figure 9-5</a>.</p><div class="figure"><a id="Bar_chart_data_updated"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject9_id307874"/><img src="httpatomoreillycomsourceoreillyimages1614809.png" alt="Bar chart, data updated"/></div></div><div class="figure-title">Figure 9-5. Bar chart, data updated</div></div><p>Good news: the values in <code class="literal">dataset</code> were modified, rebound, and used to
adjust the <code class="literal">rect</code>s. Bad news: it looks weird because we forgot to
update the labels, and also the bar colors. Good news (because I
always like to end with good news): this is easy to fix.</p><p>To ensure the colors change on update, we just copy and paste in the<a id="I_indexterm9_id307908" class="indexterm"/>
line where we set the <code class="literal">fill</code>:</p><a id="I_programlisting9_id307923"/><pre class="programlisting"><code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"y"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">h</code> <code class="o">-</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"height"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"fill"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code> <code class="c1">// <-- Down here!</code>
<code class="k">return</code> <code class="s2">"rgb(0, 0, "</code> <code class="o">+</code> <code class="p">(</code><code class="nx">d</code> <code class="o">*</code> <code class="mi">10</code><code class="p">)</code> <code class="o">+</code> <code class="s2">")"</code><code class="p">;</code>
<code class="p">});</code></pre><p>To update the labels, we use a similar pattern, only here we adjust<a id="I_indexterm9_id307933" class="indexterm"/><a id="I_indexterm9_id307943" class="indexterm"/>
their text content and x/y values:</p><a id="I_programlisting9_id307954"/><pre class="programlisting"><code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"text"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">)</code>
<code class="p">.</code><code class="nx">text</code><code class="p">(</code><code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">d</code><code class="p">;</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"x"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">,</code> <code class="nx">i</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">xScale</code><code class="p">(</code><code class="nx">i</code><code class="p">)</code> <code class="o">+</code> <code class="nx">xScale</code><code class="p">.</code><code class="nx">rangeBand</code><code class="p">()</code> <code class="o">/</code> <code class="mi">2</code><code class="p">;</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"y"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">h</code> <code class="o">-</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="o">+</code> <code class="mi">14</code><code class="p">;</code>
<code class="p">});</code></pre><p>Take a look at <span class="emphasis"><em>04_updates_all_data_fixed.html</em></span>. You’ll notice it looks
the same to start, but click the trigger and now the bar colors and
labels update correctly, as shown in <a class="xref" href="ch09.html#Updated_chart_with_correct_colors_and_labels" title="Figure 9-6. Updated chart with correct colors and labels">Figure 9-6</a>.</p><div class="figure"><a id="Updated_chart_with_correct_colors_and_labels"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject9_id307983"/><img src="httpatomoreillycomsourceoreillyimages1614810.png" alt="Updated chart with correct colors and labels"/></div></div><div class="figure-title">Figure 9-6. Updated chart with correct colors and labels</div></div></div></div><div class="sect1" title="Transitions"><div class="titlepage"><div><div><h2 class="title" style="clear: both" id="_transitions">Transitions</h2></div></div></div><p>Life transitions can be scary: the first day of school, moving to a new
city, quitting your day job to do freelance data visualization
full-time. But D3 transitions are fun, beautiful, and not at all
emotionally taxing.<a id="ix_trans" class="indexterm"/></p><p>Making a nice, super smooth, animated transition is as simple as adding<a id="I_indexterm9_id308030" class="indexterm"/>
one line of code:</p><a id="I_programlisting9_id308042"/><pre class="programlisting"><code class="p">.</code><code class="nx">transition</code><code class="p">()</code></pre><p>Specifically, add this link in the chain below where your selection is
made, and <span class="emphasis"><em>above</em></span> where any attribute changes are applied:</p><a id="I_programlisting9_id308055"/><pre class="programlisting"><code class="c1">//Update all rects</code>
<code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">)</code>
<code class="p">.</code><code class="nx">transition</code><code class="p">()</code> <code class="c1">// <-- This is new! Everything else here is unchanged.</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"y"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">h</code> <code class="o">-</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"height"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"fill"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="s2">"rgb(0, 0, "</code> <code class="o">+</code> <code class="p">(</code><code class="nx">d</code> <code class="o">*</code> <code class="mi">10</code><code class="p">)</code> <code class="o">+</code> <code class="s2">")"</code><code class="p">;</code>
<code class="p">});</code></pre><p>Now run the code in <span class="emphasis"><em>05_transition.html</em></span>, and click the text to see the
transition in action. Note that the end result looks the same visually,
but the transition from the chart’s initial state to its end state is
much, much nicer.</p><p>Isn’t that <span class="emphasis"><em>insane</em></span>? I’m not a psychologist, but I believe it is
literally insane that we can add a single line of code, and D3 will
<span class="emphasis"><em>animate</em></span> our value changes for us over time.<a id="I_indexterm9_id308084" class="indexterm"/><a id="I_indexterm9_id308091" class="indexterm"/></p><p>Without <code class="literal">transition()</code>, D3 evaluates every <code class="literal">attr()</code> statement
immediately, so the changes in height and fill happen right away. When
you add <code class="literal">transition()</code>, D3 introduces the element of time. Rather than
applying new values all at once, D3 <span class="emphasis"><em>interpolates</em></span> between the old
values and the new values, meaning it normalizes the beginning and
ending values, and calculates all their in-between states. D3 is also
smart enough to recognize and interpolate between different attribute
value formats. For example, if you specified a height of <code class="literal">200px</code> to
start but transition to just <code class="literal">100</code> (without the <code class="literal">px</code>). Or if a <code class="literal">blue</code>
fill turns <code class="literal">rgb(0,255,0)</code>. You don’t need to fret about being
consistent; D3 takes care of it.</p><p>Do you believe me yet? This is really insane. And super helpful.</p><div class="sect2" title="duration(), or How Long Is This Going to Take?"><div class="titlepage"><div><div><h3 class="title" id="_duration_or_how_long_is_this_going_to_take">duration(), or How Long Is This Going to Take?</h3></div></div></div><p>So the <code class="literal">attr()</code> values are interpolated over time, but how <span class="emphasis"><em>much</em></span> time?
It turns out the default is 250 milliseconds, or one-quarter second
(1,000 milliseconds = 1 second). That’s why the transition in
<span class="emphasis"><em>05_transition.html</em></span> is so fast.<a id="I_indexterm9_id308173" class="indexterm"/></p><p>Fortunately, you can control how much time is spent on any transition by—again, I kid you not—adding a single line of code:</p><a id="I_programlisting9_id308188"/><pre class="programlisting"><code class="p">.</code><code class="nx">duration</code><code class="p">(</code><code class="mi">1000</code><code class="p">)</code></pre><p>The <code class="literal">duration()</code> must be specified <span class="emphasis"><em>after</em></span> the <code class="literal">transition()</code>, and durations are always specified in milliseconds, so <code class="literal">duration(1000)</code> is a one-second duration.</p><p>Here is that line in context:</p><a id="I_programlisting9_id308217"/><pre class="programlisting"><code class="c1">//Update all rects</code>
<code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">)</code>
<code class="p">.</code><code class="nx">transition</code><code class="p">()</code>
<code class="p">.</code><code class="nx">duration</code><code class="p">(</code><code class="mi">1000</code><code class="p">)</code> <code class="c1">// <-- Now this is new!</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"y"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">h</code> <code class="o">-</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"height"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">);</code>
<code class="p">})</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"fill"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code>
<code class="k">return</code> <code class="s2">"rgb(0, 0, "</code> <code class="o">+</code> <code class="p">(</code><code class="nx">d</code> <code class="o">*</code> <code class="mi">10</code><code class="p">)</code> <code class="o">+</code> <code class="s2">")"</code><code class="p">;</code>
<code class="p">});</code></pre><p>Open up <span class="emphasis"><em>06_duration.html</em></span> and see the difference. Now make a copy of
that file, and try plugging in some different numbers to slow or speed
the transition. For example, try <code class="literal">3000</code> for a three-second transition,
or <code class="literal">100</code> for one-tenth of a second.</p><p>The actual durations you choose will depend on the context of your
design and what triggers the transition. In practice, I find that
transitions of around 150 ms are useful for providing minor interface
feedback (such as hovering over elements), and about 1,000 ms is ideal
for many more significant visual transitions, such as switching from one
view of the data to another. 1,000 ms (one second) is not too long, not
too short.</p><p>In case you’re feeling lazy, I made <span class="emphasis"><em>07_duration_slow.html</em></span>, which uses
<code class="literal">5000</code> for a five-second transition.</p><p>With such a slow transition, it becomes obvious that the value labels
are not transitioning smoothly along with the bar heights. As you now
know, we can correct that oversight by adding only two new lines of
code, this time in the section where we update the labels:</p><a id="I_programlisting9_id308266"/><pre class="programlisting"><code class="c1">//Update all labels</code>
<code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"text"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">)</code>
<code class="p">.</code><code class="nx">transition</code><code class="p">()</code> <code class="c1">// <-- This is new,</cod