epubjs
Version:
Render ePub documents in the browser, across many devices
180 lines (179 loc) • 118 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 6. Drawing with Data</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="ch05.html" title="Chapter 5. Data"/><link rel="next" href="ch07.html" title="Chapter 7. Scales"/></head><body><section class="chapter" title="Chapter 6. Drawing with Data" epub:type="chapter" id="drawing_with_data"><div class="titlepage"><div><div><h2 class="title">Chapter 6. Drawing with Data</h2></div></div></div><p>It’s time to start drawing with data.</p><p>Let’s continue working with our simple dataset for now:</p><a id="I_programlisting6_id298814"/><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">15</code><code class="p">,</code> <code class="mi">20</code><code class="p">,</code> <code class="mi">25</code> <code class="p">];</code></pre><div class="sect1" title="Drawing divs"><div class="titlepage"><div><div><h2 class="title" style="clear: both" id="_drawing_divs">Drawing divs</h2></div></div></div><p>We’ll use this to generate a super-simple bar chart. Bar charts are
essentially just rectangles, and an HTML <code class="literal">div</code> is the easiest way to
draw a rectangle. (Then again, to a web browser, <span class="emphasis"><em>everything</em></span> is a
rectangle, so you could easily adapt this example to use <code class="literal">span</code>s or
whatever element you prefer.)<a id="I_indexterm6_id298850" class="indexterm"/><a id="I_indexterm6_id298857" class="indexterm"/></p><p>Formally, a chart with vertically oriented rectangles is a <span class="emphasis"><em>column</em></span>
chart, and one with horizontal rectangles is a <span class="emphasis"><em>bar</em></span> chart. In practice,
most people just call them all bar charts, as I’ll do from now on.<a id="I_indexterm6_id298876" class="indexterm"/><a id="I_indexterm6_id298882" class="indexterm"/><a id="I_indexterm6_id298891" class="indexterm"/></p><p>This <code class="literal">div</code> could work well as a data bar, shown in <a class="xref" href="ch06.html#A_humble_div" title="Figure 6-1. A humble div">Figure 6-1</a>.</p><div class="figure"><a id="A_humble_div"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id298924"/><img src="httpatomoreillycomsourceoreillyimages1614753.png.jpg" alt="A humble div"/></div></div><div class="figure-title">Figure 6-1. A humble div</div></div><a id="I_programlisting6_id298942"/><pre class="programlisting"><code class="nt"><div</code> <code class="na">style=</code><code class="s">"display: inline-block;</code>
<code class="s"> width: 20px;</code>
<code class="s"> height: 75px;</code>
<code class="s"> background-color: teal;"</code><code class="nt">></div></code></pre><p>Among web standards folks, this is a semantic no-no. Normally, one
shouldn’t use an empty <code class="literal">div</code> for purely visual effect, but I am making
an exception for the sake of this example.</p><p>Because this is a <code class="literal">div</code>, its <code class="literal">width</code> and <code class="literal">height</code> are set with CSS
styles. Except for <code class="literal">height</code>, each bar in our chart will share the same
display properties, so I’ll put those shared styles into a class called
<code class="literal">bar</code>, as an embedded style up in the <code class="literal">head</code> of the document:</p><a id="I_programlisting6_id298984"/><pre class="programlisting"><code class="nt">div</code><code class="nc">.bar</code> <code class="p">{</code>
<code class="k">display</code><code class="o">:</code> <code class="k">inline</code><code class="o">-</code><code class="k">block</code><code class="p">;</code>
<code class="k">width</code><code class="o">:</code> <code class="m">20px</code><code class="p">;</code>
<code class="k">height</code><code class="o">:</code> <code class="m">75px</code><code class="p">;</code> <code class="c">/* We'll override height later */</code>
<code class="k">background-color</code><code class="o">:</code> <code class="nb">teal</code><code class="p">;</code>
<code class="p">}</code></pre><p>Now each <code class="literal">div</code> needs to be assigned the <code class="literal">bar</code> class, so our new CSS rule
will apply. If you were writing the HTML code by hand, you would write the following:</p><a id="I_programlisting6_id299003"/><pre class="programlisting"><code class="nt"><div</code> <code class="na">class=</code><code class="s">"bar"</code><code class="nt">></div></code></pre><p>Using D3, to add a class to an element, we use the <code class="literal">selection.attr()</code>
method. It’s important to understand the difference between <code class="literal">attr()</code> and
its close cousin, <code class="literal">style()</code>. <code class="literal">attr()</code> sets DOM attribute values, whereas
<code class="literal">style()</code> applies CSS styles directly to an element.<a id="I_indexterm6_id299033" class="indexterm"/><a id="I_indexterm6_id299041" class="indexterm"/></p><div class="sect2" title="Setting Attributes"><div class="titlepage"><div><div><h3 class="title" id="_setting_attributes">Setting Attributes</h3></div></div></div><p><a class="ulink" href="https://github.com/mbostock/d3/wiki/Selections#wiki-attr" target="_top"><code class="literal">attr()</code></a> is
used to set an HTML attribute and its value on an element. An HTML
attribute is any property/value pair that you could include between an<a id="I_indexterm6_id299071" class="indexterm"/>
element’s <code class="literal"><></code> brackets. For example, these HTML elements:</p><a id="I_programlisting6_id299085"/><pre class="programlisting"><code class="nt"><p</code> <code class="na">class=</code><code class="s">"caption"</code><code class="nt">></code>
<code class="nt"><select</code> <code class="na">id=</code><code class="s">"country"</code><code class="nt">></code>
<code class="nt"><img</code> <code class="na">src=</code><code class="s">"logo.png"</code> <code class="na">width=</code><code class="s">"100px"</code> <code class="na">alt=</code><code class="s">"Logo"</code> <code class="nt">/></code></pre><p>contain a total of five attributes (and corresponding values), all of
which could be set with <code class="literal">attr()</code>:</p><div class="informaltable"><table style="width: 50%; border-collapse: collapse; border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; border-left: 0.5pt solid ; border-right: 0.5pt solid ; "><colgroup><col class="col_1"/><col class="col_2"/></colgroup><thead><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> Attribute </td><td style="border-bottom: 0.5pt solid ; "> Value</td></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "><p><code class="literal">class</code></p></td><td style="border-bottom: 0.5pt solid ; "><p><code class="literal">caption</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "><p><code class="literal">id</code></p></td><td style="border-bottom: 0.5pt solid ; "><p><code class="literal">country</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "><p><code class="literal">src</code></p></td><td style="border-bottom: 0.5pt solid ; "><p><code class="literal">logo.png</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "><p><code class="literal">width</code></p></td><td style="border-bottom: 0.5pt solid ; "><p><code class="literal">100px</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; "><p><code class="literal">alt</code></p></td><td><p><code class="literal">Logo</code></p></td></tr></tbody></table></div><p>To assign a class of <code class="literal">bar</code>, we can use:</p><a id="I_programlisting6_id299241"/><pre class="programlisting"><code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"class"</code><code class="p">,</code> <code class="s2">"bar"</code><code class="p">)</code></pre></div><div class="sect2" title="A Note on Classes"><div class="titlepage"><div><div><h3 class="title" id="_a_note_on_classes">A Note on Classes</h3></div></div></div><p>Note that an element’s <span class="emphasis"><em>class</em></span> is stored as an HTML attribute. The class,
in turn, is used to reference a CSS style rule. This could cause some
confusion because there is a difference between setting a <span class="emphasis"><em>class</em></span> (from
which styles are inferred) and applying a <span class="emphasis"><em>style</em></span> directly to an
element. You can do both with D3. Although you should use whatever
approach makes the most sense to you, I recommend using <span class="emphasis"><em>classes</em></span> for
properties that are shared by multiple elements, and applying <span class="emphasis"><em>style</em></span>
rules directly only when deviating from the norm. (In fact, that’s what
we’ll do in just a moment.)<a id="I_indexterm6_id299284" class="indexterm"/><a id="I_indexterm6_id299292" class="indexterm"/></p><p>I also want to briefly mention another D3 method, <code class="literal">classed()</code>, which can
be used to quickly apply or remove classes from elements. The preceding line of<a id="I_indexterm6_id299308" class="indexterm"/>
code could be rewritten as the following:</p><a id="I_programlisting6_id299317"/><pre class="programlisting"><code class="p">.</code><code class="nx">classed</code><code class="p">(</code><code class="s2">"bar"</code><code class="p">,</code> <code class="kc">true</code><code class="p">)</code></pre><p>This line simply takes whatever selection is passed to it and applies
the class <code class="literal">bar</code>. If <code class="literal">false</code> were specified, it would do the opposite,
removing the class of <code class="literal">bar</code> from any elements in the selection:</p><a id="I_programlisting6_id299338"/><pre class="programlisting"><code class="p">.</code><code class="nx">classed</code><code class="p">(</code><code class="s2">"bar"</code><code class="p">,</code> <code class="kc">false</code><code class="p">)</code></pre></div><div class="sect2" title="Back to the Bars"><div class="titlepage"><div><div><h3 class="title" id="_back_to_the_bars">Back to the Bars</h3></div></div></div><p>Putting it all together with our dataset, here is the complete D3 code
so far:</p><a id="I_programlisting6_id299357"/><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">15</code><code class="p">,</code> <code class="mi">20</code><code class="p">,</code> <code class="mi">25</code> <code class="p">];</code>
<code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"body"</code><code class="p">).</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"div"</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">"div"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"class"</code><code class="p">,</code> <code class="s2">"bar"</code><code class="p">);</code></pre><p>To see what’s going on, look at <span class="emphasis"><em>01_drawing_divs.html</em></span> in your browser,
view the source, and open your web inspector. You should see five
vertical <code class="literal">div</code> bars, one generated for each point in our dataset.
However, with no space between them, they look like one big rectangle, as seen in Figures <a class="xref" href="ch06.html#Five_divs_masquerading_as_one" title="Figure 6-2. Five divs masquerading as one">6-2</a> and <a class="xref" href="ch06.html#Five_divs_masquerading_as_one_inspector" title="Figure 6-3. Five divs masquerading as one, as seen through the web inspector">6-3</a>.</p><div class="figure"><a id="Five_divs_masquerading_as_one"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id299401"/><img src="httpatomoreillycomsourceoreillyimages1614754.png.jpg" alt="Five divs masquerading as one"/></div></div><div class="figure-title">Figure 6-2. Five <code class="literal">div</code>s masquerading as one</div></div><div class="figure"><a id="Five_divs_masquerading_as_one_inspector"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id299430"/><img src="httpatomoreillycomsourceoreillyimages1614755.png" alt="Five divs masquerading as one, as seen through the web inspector"/></div></div><div class="figure-title">Figure 6-3. Five <code class="literal">div</code>s masquerading as one, as seen through the web inspector</div></div></div><div class="sect2" title="Setting Styles"><div class="titlepage"><div><div><h3 class="title" id="_setting_styles">Setting Styles</h3></div></div></div><p>The <a class="ulink" href="https://github.com/mbostock/d3/wiki/Selections#wiki-style" target="_top"><code class="literal">style()</code></a>
method is used to apply a CSS property and value directly to an HTML<a id="I_indexterm6_id299470" class="indexterm"/>
element. This is the equivalent of including CSS rules within a <code class="literal">style</code>
attribute right in your HTML, as in:</p><a id="I_programlisting6_id299485"/><pre class="programlisting"><code class="nt"><div</code> <code class="na">style=</code><code class="s">"height: 75px;"</code><code class="nt">></div></code></pre><p>To make a bar chart, the height of each bar must be a function of its
corresponding data value. So let’s add this to the end of our D3 code (taking care to keep the final semicolon at the very end of the chain):</p><a id="I_programlisting6_id299496"/><pre class="programlisting"><code class="p">.</code><code class="nx">style</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">d</code> <code class="o">+</code> <code class="s2">"px"</code><code class="p">;</code>
<code class="p">});</code></pre><p>See that code in <span class="emphasis"><em>02_drawing_divs_height.html</em></span>. You should see a very small bar
chart, like the one in <a class="xref" href="ch06.html#A_small_bar_chart" title="Figure 6-4. A small bar chart">Figure 6-4</a>.</p><div class="figure"><a id="A_small_bar_chart"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id299522"/><img src="httpatomoreillycomsourceoreillyimages1614756.png.jpg" alt="A small bar chart"/></div></div><div class="figure-title">Figure 6-4. A small bar chart</div></div><p>When D3 loops through each data point, the value of <code class="literal">d</code> will be set to
that of the corresponding value. So we are setting a <code class="literal">height</code> value of
<code class="literal">d</code> (the current data value) while appending the text <code class="literal">px</code> (to specify
the units are pixels). The resulting heights are <code class="literal">5px</code>, <code class="literal">10px</code>, <code class="literal">15px</code>,
<code class="literal">20px</code>, and <code class="literal">25px</code>.</p><p>This looks a little bit silly, so let’s make those bars taller:</p><a id="I_programlisting6_id299585"/><pre class="programlisting"><code class="p">.</code><code class="nx">style</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="kd">var</code> <code class="nx">barHeight</code> <code class="o">=</code> <code class="nx">d</code> <code class="o">*</code> <code class="mi">5</code><code class="p">;</code> <code class="c1">//Scale up by factor of 5</code>
<code class="k">return</code> <code class="nx">barHeight</code> <code class="o">+</code> <code class="s2">"px"</code><code class="p">;</code>
<code class="p">});</code></pre><p>Add some space to the right of each bar (in the embedded CSS style, in the document <code class="literal">head</code>), to space things out:</p><a id="I_programlisting6_id299600"/><pre class="programlisting"><code class="nt">margin-right</code><code class="o">:</code> <code class="nt">2px</code><code class="o">;</code></pre><p>Nice! We could go to <a class="ulink" href="https://www.siggraph.org" target="_top">SIGGRAPH</a> with that chart (<a class="xref" href="ch06.html#A_taller_bar_chart" title="Figure 6-5. A taller bar chart">Figure 6-5</a>).</p><div class="figure"><a id="A_taller_bar_chart"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id299628"/><img src="httpatomoreillycomsourceoreillyimages1614757.png.jpg" alt="A taller bar chart"/></div></div><div class="figure-title">Figure 6-5. A taller bar chart</div></div><p>Try out the sample code <span class="emphasis"><em>03_drawing_divs_spaced.html</em></span>. Again, view the source
and use the web inspector to contrast the original HTML against the
final DOM.<a id="I_indexterm6_id299653" class="indexterm"/></p></div></div><div class="sect1" title="The Power of data()"><div class="titlepage"><div><div><h2 class="title" style="clear: both" id="_the_power_of_data">The Power of data()</h2></div></div></div><p>This is exciting, but real-world data is never this clean:</p><a id="I_programlisting6_id299674"/><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">15</code><code class="p">,</code> <code class="mi">20</code><code class="p">,</code> <code class="mi">25</code> <code class="p">];</code></pre><p>Let’s make our data a bit messier, as in <span class="emphasis"><em>04_power_of_data.html</em></span>:</p><a id="I_programlisting6_id299688"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">dataset</code> <code class="o">=</code> <code class="p">[</code> <code class="mi">25</code><code class="p">,</code> <code class="mi">7</code><code class="p">,</code> <code class="mi">5</code><code class="p">,</code> <code class="mi">26</code><code class="p">,</code> <code class="mi">11</code> <code class="p">];</code></pre><p>That change in data results in the bars shown in <a class="xref" href="ch06.html#New_data_values" title="Figure 6-6. New data values">Figure 6-6</a>. We’re not limited to five data points, of course. Let’s add many more!
(See <span class="emphasis"><em>05_power_of_data_more_points.html</em></span>.)</p><a id="I_programlisting6_id299707"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">dataset</code> <code class="o">=</code> <code class="p">[</code> <code class="mi">25</code><code class="p">,</code> <code class="mi">7</code><code class="p">,</code> <code class="mi">5</code><code class="p">,</code> <code class="mi">26</code><code class="p">,</code> <code class="mi">11</code><code class="p">,</code> <code class="mi">8</code><code class="p">,</code> <code class="mi">25</code><code class="p">,</code> <code class="mi">14</code><code class="p">,</code> <code class="mi">23</code><code class="p">,</code> <code class="mi">19</code><code class="p">,</code>
<code class="mi">14</code><code class="p">,</code> <code class="mi">11</code><code class="p">,</code> <code class="mi">22</code><code class="p">,</code> <code class="mi">29</code><code class="p">,</code> <code class="mi">11</code><code class="p">,</code> <code class="mi">13</code><code class="p">,</code> <code class="mi">12</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">10</code><code class="p">,</code>
<code class="mi">24</code><code class="p">,</code> <code class="mi">18</code><code class="p">,</code> <code class="mi">25</code><code class="p">,</code> <code class="mi">9</code><code class="p">,</code> <code class="mi">3</code> <code class="p">];</code></pre><div class="figure"><a id="New_data_values"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id299721"/><img src="httpatomoreillycomsourceoreillyimages1614758.png.jpg" alt="New data values"/></div></div><div class="figure-title">Figure 6-6. New data values</div></div><p>Twenty-five data points instead of five (see <a class="xref" href="ch06.html#Lots_more_data_values" title="Figure 6-7. Lots more data values">Figure 6-7</a>)!</p><div class="figure"><a id="Lots_more_data_values"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id299756"/><img src="httpatomoreillycomsourceoreillyimages1614759.png" alt="Lots more data values"/></div></div><div class="figure-title">Figure 6-7. Lots more data values</div></div><p>How does D3 automatically expand our
chart as needed?<a id="I_indexterm6_id299777" class="indexterm"/></p><a id="I_programlisting6_id299784"/><pre class="programlisting"><code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"body"</code><code class="p">).</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"div"</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">// <-- The answer is here!</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">"div"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"class"</code><code class="p">,</code> <code class="s2">"bar"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">style</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="kd">var</code> <code class="nx">barHeight</code> <code class="o">=</code> <code class="nx">d</code> <code class="o">*</code> <code class="mi">5</code><code class="p">;</code>
<code class="k">return</code> <code class="nx">barHeight</code> <code class="o">+</code> <code class="s2">"px"</code><code class="p">;</code>
<code class="p">});</code></pre><p>Give <code class="literal">data()</code> 10 values, and it will loop through 10 times. Give it
one million values, and it will loop through one million times. (Just be
patient.)</p><p>That is the power of <code class="literal">data()</code>—being smart enough to loop through the
full length of whatever dataset you throw at it, executing each method
beneath it in the chain, while updating the context in which each method
operates, so <code class="literal">d</code> always refers to the current datum at that point in the
loop.</p><p>That might be a mouthful, and if it all doesn’t make sense yet, it will
soon. I encourage you to make a copy of <span class="emphasis"><em>05_power_of_data_more_points.html</em></span>, tweak the <code class="literal">dataset</code> values, and note how the bar chart changes.</p><p>Remember, the <span class="emphasis"><em>data</em></span> is driving the visualization—not the other way
around.</p><div class="sect2" title="Random Data"><div class="titlepage"><div><div><h3 class="title" id="_random_data">Random Data</h3></div></div></div><p>Sometimes it’s fun to generate random data values, whether for testing<a id="I_indexterm6_id299848" class="indexterm"/>
purposes or just pure geekiness. That’s just what I’ve done in
<span class="emphasis"><em>06_power_of_data_random.html</em></span>. Notice that each time you reload the page, the
bars render differently, as shown in <a class="xref" href="ch06.html#Bar_charts_with_random_values" title="Figure 6-8. Bar charts with random values">Figure 6-8</a>.</p><div class="figure"><a id="Bar_charts_with_random_values"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id299873"/><img src="httpatomoreillycomsourceoreillyimages1614760.png" alt="Bar charts with random values"/></div></div><div class="figure-title">Figure 6-8. Bar charts with random values</div></div><p>View the source, and you’ll see this code:</p><a id="I_programlisting6_id299896"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">dataset</code> <code class="o">=</code> <code class="p">[];</code> <code class="c1">//Initialize empty array</code>
<code class="k">for</code> <code class="p">(</code><code class="kd">var</code> <code class="nx">i</code> <code class="o">=</code> <code class="mi">0</code><code class="p">;</code> <code class="nx">i</code> <code class="o"><</code> <code class="mi">25</code><code class="p">;</code> <code class="nx">i</code><code class="o">++</code><code class="p">)</code> <code class="p">{</code> <code class="c1">//Loop 25 times</code>
<code class="kd">var</code> <code class="nx">newNumber</code> <code class="o">=</code> <code class="nb">Math</code><code class="p">.</code><code class="nx">random</code><code class="p">()</code> <code class="o">*</code> <code class="mi">30</code><code class="p">;</code> <code class="c1">//New random number (0-30)</code>
<code class="nx">dataset</code><code class="p">.</code><code class="nx">push</code><code class="p">(</code><code class="nx">newNumber</code><code class="p">);</code> <code class="c1">//Add new number to array</code>
<code class="p">}</code></pre><p>That code doesn’t use any D3 methods; it’s just JavaScript. Without
going into too much detail, this code does the following:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">
Creates an empty array called <code class="literal">dataset</code>.
</li><li class="listitem">
Initiates a <code class="literal">for</code> loop, which is executed 25 times.
</li><li class="listitem">
Each time, it generates a new random number with a value between 0 and 30. (Well, technically, <span class="emphasis"><em>almost</em></span> 30. <code class="literal">Math.random()</code> returns values as low as 0.0 all the way up to, but not including, 1.0. So if <code class="literal">Math.random()</code> returned 0.99999, then the result would be 0.99999 times 30, which is 29.9997, or the teensiest bit less than 30.)
</li><li class="listitem">
That new number is appended to the <code class="literal">dataset</code> array. (<code class="literal">push()</code> is an array method that appends a new value to the end of an array.)
</li></ol></div><p>Just for kicks, open up the JavaScript console and enter <code class="literal">console.log(dataset)</code>. You should see the full array of 25 randomized data values, as shown in <a class="xref" href="ch06.html#Random_values_in_console" title="Figure 6-9. Random values in console">Figure 6-9</a>.</p><div class="figure"><a id="Random_values_in_console"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id299994"/><img src="httpatomoreillycomsourceoreillyimages1614761.png" alt="Random values in console"/></div></div><div class="figure-title">Figure 6-9. Random values in console</div></div><p>Notice that they are all decimal or floating point values (such as 14.793717765714973), not whole numbers or integers (such as 14) like we used initially. For this example, decimal values are fine, but if you ever need whole numbers, you could use JavaScript’s <code class="literal">Math.round()</code> or <code class="literal">Math.floor()</code> methods. <code class="literal">Math.round()</code> rounds any number to the nearest integer, whereas <code class="literal">Math.floor()</code> always rounds down, for greater control over the result. For example, you could wrap the random number generator from this line:</p><a id="I_programlisting6_id300034"/><pre class="programlisting"> <code class="kd">var</code> <code class="nx">newNumber</code> <code class="o">=</code> <code class="nb">Math</code><code class="p">.</code><code class="nx">random</code><code class="p">()</code> <code class="o">*</code> <code class="mi">30</code><code class="p">;</code></pre><p>as follows:</p><a id="I_programlisting6_id300043"/><pre class="programlisting"> <code class="kd">var</code> <code class="nx">newNumber</code> <code class="o">=</code> <code class="nb">Math</code><code class="p">.</code><code class="nx">floor</code><code class="p">(</code><code class="nb">Math</code><code class="p">.</code><code class="nx">random</code><code class="p">()</code> <code class="o">*</code> <code class="mi">30</code><code class="p">);</code></pre><p>Using this code, <code class="literal">newNumber</code> would always be either 0 or 29, or any integer in between. Why not 30? Because <code class="literal">Math.random()</code> always returns values <span class="emphasis"><em>less than</em></span> 1.0, and <code class="literal">Math.floor()</code> will always <span class="emphasis"><em>round down</em></span>, so 29 is the highest possible return value.</p><p>Try it out in <span class="emphasis"><em>07_power_of_data_rounded.html</em></span>, and use the console to verify that the numbers have indeed been rounded to integers, as displayed in <a class="xref" href="ch06.html#Random_integer_values_in_console" title="Figure 6-10. Random integer values in console">Figure 6-10</a>.</p><div class="figure"><a id="Random_integer_values_in_console"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id300093"/><img src="httpatomoreillycomsourceoreillyimages1614762.png" alt="Random integer values in console"/></div></div><div class="figure-title">Figure 6-10. Random integer values in console</div></div><p>That’s about all we can do visually with <code class="literal">div</code>s. Let’s expand our visual
possibilities with SVG.</p></div></div><div class="sect1" title="Drawing SVGs"><div class="titlepage"><div><div><h2 class="title" style="clear: both" id="_drawing_svgs">Drawing SVGs</h2></div></div></div><p>For a quick refresher on SVG syntax, see <a class="xref" href="ch03.html#SVG_3" title="SVG">SVG</a>.</p><p>One thing you might notice about SVG elements is that all of their
properties are specified as <span class="emphasis"><em>attributes</em></span>. That is, they are included as<a id="I_indexterm6_id300145" class="indexterm"/><a id="ix_svgshapes" class="indexterm"/>
property/value pairs within each element tag, like this:</p><a id="I_programlisting6_id300170"/><pre class="programlisting"><code class="nt"><element</code> <code class="na">property=</code><code class="s">"value"</code><code class="nt">></element></code></pre><p>Hmm, that looks strangely like HTML!</p><a id="I_programlisting6_id300181"/><pre class="programlisting"><code class="nt"><p</code> <code class="na">class=</code><code class="s">"eureka"</code><code class="nt">></code>Eureka!<code class="nt"></p></code></pre><p>We have already used D3’s handy <code class="literal">append()</code> and <code class="literal">attr()</code> methods to
create new HTML elements and set their attributes. Because SVG elements
exist in the DOM, just as HTML elements do, we can use <code class="literal">append()</code> and
<code class="literal">attr()</code> in exactly the same way to generate SVG images.<a id="I_indexterm6_id300207" class="indexterm"/><a id="I_indexterm6_id300214" class="indexterm"/></p><div class="sect2" title="Create the SVG"><div class="titlepage"><div><div><h3 class="title" id="_create_the_svg">Create the SVG</h3></div></div></div><p>First, we need to create the SVG element in which to place all our
shapes:</p><a id="I_programlisting6_id300235"/><pre class="programlisting"><code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"body"</code><code class="p">).</code><code class="nx">append</code><code class="p">(</code><code class="s2">"svg"</code><code class="p">);</code></pre><p>That will find the document’s <code class="literal">body</code> and append a new <code class="literal">svg</code> element just
before the closing <code class="literal"></body></code> tag. That code will work, but I’d like to
suggest a slight modification:</p><a id="I_programlisting6_id300255"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">svg</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"body"</code><code class="p">).</code><code class="nx">append</code><code class="p">(</code><code class="s2">"svg"</code><code class="p">);</code></pre><p>Remember how most D3 methods return a reference to the DOM element on
which they act? By creating a new variable <code class="literal">svg</code>, we are able to capture
the reference handed back by <code class="literal">append()</code>. Think of <code class="literal">svg</code> not as a
variable but as a reference pointing to the SVG object that we just
created. This reference will save us a lot of code later. Instead of
having to search for that SVG each time—as in <code class="literal">d3.select("svg")</code>—we
just say <code class="literal">svg</code>:</p><a id="I_programlisting6_id300287"/><pre class="programlisting"><code class="nx">svg</code><code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"width"</code><code class="p">,</code> <code class="mi">500</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="mi">50</code><code class="p">);</code></pre><p>Alternatively, that could all be written as one line of code:</p><a id="I_programlisting6_id300297"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">svg</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"body"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"svg"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"width"</code><code class="p">,</code> <code class="mi">500</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="mi">50</code><code class="p">);</code></pre><p>See <span class="emphasis"><em>08_drawing_svgs.html</em></span> for that code. Inspect the DOM and notice that
there is, indeed, an empty SVG element.</p><p>To simplify your life, I recommend putting the width and height values<a id="I_indexterm6_id300315" class="indexterm"/>
into variables at the top of your code, as in <span class="emphasis"><em>09_drawing_svgs_size.html</em></span>.
View the source, and you’ll see the following code:</p><a id="I_programlisting6_id300330"/><pre class="programlisting"><code class="c1">//Width and height</code>
<code class="kd">var</code> <code class="nx">w</code> <code class="o">=</code> <code class="mi">500</code><code class="p">;</code>
<code class="kd">var</code> <code class="nx">h</code> <code class="o">=</code> <code class="mi">50</code><code class="p">;</code></pre><p>I’ll be doing that with all future examples. By <span class="emphasis"><em>variabalizing</em></span> the size
values, they can be easily referenced throughout your code, as in the following:</p><a id="I_programlisting6_id300344"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">svg</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"body"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"svg"</code><code class="p">)</code>
<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="p">)</code> <code class="c1">// <-- Here</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="nx">h</code><code class="p">);</code> <code class="c1">// <-- and here!</code></pre><p>Also, if you send me a petition to make “variabalize” a real word, I
will gladly sign it.</p></div><div class="sect2" title="Data-Driven Shapes"><div class="titlepage"><div><div><h3 class="title" id="_data_driven_shapes">Data-Driven Shapes</h3></div></div></div><p>Time to add some shapes. I’ll bring back our trusty old dataset: <a id="I_indexterm6_id300371" class="indexterm"/></p><a id="I_programlisting6_id300379"/><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">15</code><code class="p">,</code> <code class="mi">20</code><code class="p">,</code> <code class="mi">25</code> <code class="p">];</code></pre><p>and then use <code class="literal">data()</code> to iterate through each data point, creating a
<code class="literal">circle</code> for each one:</p><a id="I_programlisting6_id300396"/><pre class="programlisting"><code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"circle"</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">"circle"</code><code class="p">);</code></pre><p>Remember, <code class="literal">selectAll()</code> will return empty references to all <code class="literal">circle</code>s
(which don’t exist yet), <code class="literal">data()</code> binds our data to the elements we’re
about to create, <code class="literal">enter()</code> returns a placeholder reference to the new
element, and <code class="literal">append()</code> finally adds a <code class="literal">circle</code> to the DOM. In this
case, it appends those <code class="literal">circle</code>s to the end of the SVG element, as
our initial selection is our reference <code class="literal">svg</code> (as opposed to the document
<code class="literal">body</code>, for example).</p><p>To make it easy to reference all of the <code class="literal">circle</code>s later, we can create a
new variable to store references to them all:</p><a id="I_programlisting6_id300448"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">circles</code> <code class="o">=</code> <code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"circle"</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">"circle"</code><code class="p">);</code></pre><p>Great, but all these circles still need positions and sizes, displayed in <a class="xref" href="ch06.html#Row_of_data_circles" title="Figure 6-11. Row of data circles">Figure 6-11</a>. Be warned,
the following code might blow your mind:</p><a id="I_programlisting6_id300465"/><pre class="programlisting"><code class="nx">circles</code><code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"cx"</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="p">(</code><code class="nx">i</code> <code class="o">*</code> <code class="mi">50</code><code class="p">)</code> <code class="o">+</code> <code class="mi">25</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">"cy"</code><code class="p">,</code> <code class="nx">h</code><code class="o">/</code><code class="mi">2</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"r"</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></pre><div class="figure"><a id="Row_of_data_circles"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject6_id300480"/><img src="httpatomoreillycomsourceoreillyimages1614763.png.jpg" alt="Row of data circles"/></div></div><div class="figure-title">Figure 6-11. Row of data circles</div></div><p>Feast your eyes on the demo <span class="emphasis"><em>10_drawing_svgs_circles.html</em></span>. Let’s step through
the code, one line at a time:</p><a id="I_programlisting6_id300506"/><pre class="programlisting"><code class="nx">circles</code><code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"cx"</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="p">(</code><code class="nx">i</code> <code class="o">*</code> <code class="mi">50</code><code class="p">)</code> <code class="o">+</code> <code class="mi">25</code><code class="p">;</code>
<code class="p">})</code></pre><p>This takes the reference to all <code class="literal">circle</code>s and sets the <code class="literal">cx</code> attribute
for each one. (Remember that, in SVG lingo, <code class="literal">cx</code> is the x position value
of the <span class="emphasis"><em>center</em></span> of the circle.) Our data has already been bound to the
<code class="literal">circle</code> elements, so for each <code class="literal">circle</code>, the value <code class="literal">d</code> matches the
corresponding value in our original dataset (5, 10, 15, 20, or 25).</p><p>Another value, <code class="literal">i</code>, is also automatically populated for us. (Thanks,
D3!) Just as with <code class="literal">d</code>, the name <code class="literal">i</code> here is arbitrary and could be set to whatever you like, such as <code class="literal">counter</code> or <code class="literal">elementID</code>. I prefer to use <code class="literal">i</code> because it is concise, it alludes to the convention of using <code class="literal">i</code> in <code class="literal">for</code> loops, and it is very common, as you’ll see it in all the online examples.</p><p>So, <code class="literal">i</code> is a numeric index value of the current element. Counting starts at zero, so for our “first” circle <code class="literal">i == 0</code>, the second circle’s <code class="literal">i == 1</code>, and so on. We’re using <code class="literal">i</code> to push each subsequent circle over to the right, because each subsequent loop through, the value of <code class="literal">i</code> increases by one:</p><a id="I_programlisting6_id300602"/><pre class="programlisting"><code class="p">(</code><code class="mi">0</code> <code class="o">*</code> <code class="mi">50</code><code class="p">)</code> <code class="o">+</code> <code class="mi">25</code> <code class="c1">//Returns 25</code>
<code class="p">(</code><code class="mi">1</code> <code class="o">*</code> <code class="mi">50</code><code class="p">)</code> <code class="o">+</code> <code class="mi">25</code> <code class="c1">//Returns 75</code>
<code class="p">(</code><code class="mi">2</code> <code class="o">*</code> <code class="mi">50</code><code class="p">)</code> <code class="o">+</code> <code class="mi">25</code> <code class="c1">//Returns 125</code>
<code class="p">(</code><code class="mi">3</code> <code class="o">*</code> <code class="mi">50</code><code class="p">)</code> <code class="o">+</code> <code class="mi">25</code> <code class="c1">//Returns 175</code>
<code class="p">(</code><code class="mi">4</code> <code class="o">*</code> <code class="mi">50</code><code class="p">)</code> <code class="o">+</code> <code class="mi">25</code> <code class="c1">//Returns 225</code></pre><p>To make sure <code class="literal">i</code> is available to your custom function, you must include
it as an argument in the function definition, <code class="literal">function(d, i)</code>. You must
also include <code class="literal">d</code>, even if you don’t use <code class="literal">d</code> within your function (as in
the preceding case). This is because, again, the actual names used for these arguments are not important, but the total number of arguments (one or two) is.</p><p>Also, in case you’re feeling a surge of parameter anxiety, don’t worry. You’ll only ever have to worry about <code class="literal">d</code> and <code class="literal">i</code>. There are no additional anonymous function parameters to learn about later.</p><p>On to the next line:</p><a id="I_programlisting6_id300646"/><pre class="programlisting"><code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"cy"</code><code class="p">,</code> <code class="nx">h</code><code class="o">/</code><code class="mi">2</code><code class="p">)</code></pre><p><code class="literal">cy</code> is the y position value of the center of each circle. We’re setting
<code class="literal">cy</code> to <code class="literal">h</code> divided by two, or one-half of <code class="literal">h</code>. You’ll recall that <code class="literal">h</code>
stores the height of the entire SVG, so <code class="literal">h/2</code> has the effect of aligning
all <code class="literal">circle</code>s in the vertical center of the image:</p><a id="I_programlisting6_id300683"/><pre class="programlisting"><code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"r"</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></pre><p>Finally, the radius <code class="literal">r</code> of each <code class="literal">circle</code> is simply set to <code class="literal">d</code>, the
corresponding data value.</p></div><div class="sect2" title="Pretty Colors, Oooh!"><div class="titlepage"><div><div><h3 class="title" id="_pretty_colors_oooh">Pretty Colors, Oooh!</h3></div></div></div><p>Color fills and strokes are just other attributes that you can set using<a id="I_indexterm6_id300713" class="indexterm"/>
the same methods. Simply by appending this code:</p><a id="I_programlisting6_id300723"/><pre class="programlisting"><code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"fill"</code><code class="p">,</code> <code class="s2">"yellow"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"stroke"</code><code class="p">,</code> <code class="s2">"orange"</code><code class="p">)</code>
<code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"stroke-width"</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">retu