UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

263 lines (260 loc) 165 kB
<?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">// &lt;-- 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">&lt;p&gt;</code>Click on this text to update the chart with new data values (once).<code class="nt">&lt;/p&gt;</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">// &lt;-- 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">// &lt;-- 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">// &lt;-- 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">// &lt;-- This is new,</cod