topical media & game development

talk show tell print

mobile-data-circle-index.htm / htm



  <!DOCTYPE html>
  <!-- saved from url=(0049)http://mbostock.github.io/d3/tutorial/circle.html -->
  <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      
      <title>d3.js ~ Three Little Circles</title>
      <script src="http://d3js.org/d3.v3.min.js"></script>
   </head>
   <body>
      <div class="body">
        <div class="content">
  
  <h1 id="three_little_circles">three little circles</h1>
  <style type="text/css">
  
  .chart {
    background: #fff;
    border: solid 1px #ddd;
    box-shadow: 0 0 4px rgba(0,0,0,.2);
    font: 10px sans-serif;
    height: 180px;
    position: relative;
    width: 720px;
  }
  
  .chart svg {
    border-left: solid 2px #ddd;
    left: 360px;
    position: absolute;
    top: 0;
  }
  
  .chart pre {
    font: 12px monospace;
    height: 60px;
    left: 10px;
    position: absolute;
    top: 0;
    width: 340px;
  }
  
  .chart circle.little {
    fill: #aaa;
    stroke: #666;
    stroke-width: 1.5px;
  }
  
  .chart button {
    left: 275px;
    position: absolute;
    top: 145px;
    width: 80px;
  }
  
  .chart .data rect {
    fill: #eee;
    stroke: #ccc;
  }
  
  </style>
  <script type="text/javascript">
  var data = [32, 57, 112],
      dataEnter = data.concat(293),
      dataExit = data.slice(0, 2),
      w = 360,
      h = 180,
      x = d3.scale.ordinal().domain([57, 32, 112]).rangePoints([0, w], 1),
      y = d3.scale.ordinal().domain(data).rangePoints([0, h], 2);
  </script>
  <p>Once upon a time, there were three little circles. This tutorial shows you how to manipulate them using selections.</p>
  
  <h2 id="selecting_elements">selecting elements</h2>
  
  <p>The <a href="https://github.com/mbostock/d3/wiki/Selections#selectAll">selectAll</a> operator takes a <a href="http://www.w3.org/TR/CSS2/selector.html">selector</a> string, such as “circle”, and returns a selection:</p>
  <div class="chart" id="chart-2">
  <pre class="code">var circle = svg.selectAll("circle");
  </pre>
  <button>run</button>
  <svg width="360" height="180"><circle class="little" cx="180" cy="45" r="12"></circle><circle class="little" cx="60" cy="90" r="12"></circle><circle class="little" cx="300" cy="135" r="12"></circle></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-2").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    svg.selectAll(".little")
        .data(data)
      .enter().append("circle")
        .attr("class", "little")
        .attr("cx", x)
        .attr("cy", y)
        .attr("r", 12);
  
    d3.select("#chart-2 button").on("click", function() {
      svg.selectAll(".select").remove();
  
      svg.selectAll(".select")
          .data(data)
        .enter().append("circle")
          .attr("class", "select")
          .attr("cx", x)
          .attr("cy", y)
          .attr("r", 60)
          .style("fill", "none")
          .style("stroke", "red")
          .style("stroke-opacity", 1e-6)
          .style("stroke-width", 3)
        .transition()
          .duration(750)
          .attr("r", 12)
          .style("stroke-opacity", 1);
    });
  })();
  </script>
  <p>Once we have a selection, we can apply various operators to the selected elements. For example, we might change the fill color using <a href="https://github.com/mbostock/d3/wiki/Selections#style">style</a>, and the radius and the <em>y</em>-position using <a href="https://github.com/mbostock/d3/wiki/Selections#attr">attr</a>:</p>
  <div class="chart" id="chart-3">
  <pre class="code">circle.style("fill", "steelblue");
  circle.attr("cy", 90);
  circle.attr("r", 30);
  </pre>
  <button>run</button>
  <svg width="360" height="180"><circle class="little" cx="180" cy="45" r="12"></circle><circle class="little" cx="60" cy="90" r="12"></circle><circle class="little" cx="300" cy="135" r="12"></circle></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-3").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var circle = svg.selectAll(".little")
        .data(data)
      .enter().append("circle")
        .attr("class", "little")
        .attr("cx", x)
        .attr("cy", y)
        .attr("r", 12);
  
    d3.select("#chart-3 button").on("click", function() {
      circle.style("fill", "#aaa").attr("r", 12).attr("cy", y);
      circle.transition().duration(500).delay(0).style("fill", "steelblue");
      circle.transition().duration(500).delay(500).attr("cy", 90);
      circle.transition().duration(500).delay(1000).attr("r", 30);
    });
  })();
  </script>
  <p>We can also compute the attribute values dynamically, using functions rather than constants. For example, maybe we want to set the <em>x</em>-coordinate to a random value:</p>
  <div class="chart" id="chart-4">
  <pre class="code">circle.attr("cx", function() {
    return Math.random() * w;
  });
  </pre>
  <button>run</button>
  <svg width="360" height="180"><circle class="little" cx="180" cy="45" r="12"></circle><circle class="little" cx="60" cy="90" r="12"></circle><circle class="little" cx="300" cy="135" r="12"></circle></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-4").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var circle = svg.selectAll(".little")
        .data(data)
      .enter().append("circle")
        .attr("class", "little")
        .attr("cx", x)
        .attr("cy", y)
        .attr("r", 12);
  
    d3.select("#chart-4 button").on("click", function() {
      circle.transition()
          .duration(750)
          .attr("cx", function() { return Math.random() * w; });
    });
  })();
  </script>
  <p>If you run this example multiple times, you’ll see that the attribute is recomputed as a number random number each time. Unlike Protovis, D3 doesn’t stash these functions internally; they are run once, immediately, and then your code continues. So you can run them again or redefine them however you like.</p>
  
  <h2 id="binding_data">binding data</h2>
  
  <p>This is beginning to look a lot like <a href="http://jquery.com/">jQuery</a>. More commonly, though, we want to use <em>data</em> to drive the appearance of our circles. To do that, we need some data. For the sake of example, let’s imagine that each of these circles represents a number: 32, 57 and 112. The <a href="https://github.com/mbostock/d3/wiki/Selections#data">data</a> operator binds these numbers to the circles:</p>
  <div class="chart" id="chart-5">
  <pre class="code">circle.data([32, 57, 112]);
  </pre>
  <button>run</button>
  <svg width="360" height="180"><circle class="little" cx="180" cy="45" r="12"></circle><circle class="little" cx="60" cy="90" r="12"></circle><circle class="little" cx="300" cy="135" r="12"></circle><g class="data" transform="translate(20,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">32</text></g><g class="data" transform="translate(40,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">57</text></g><g class="data" transform="translate(60,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">112</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-5").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    svg.selectAll(".little")
        .data(data)
      .enter().append("circle")
        .attr("class", "little")
        .attr("cx", x)
        .attr("cy", y)
        .attr("r", 12);
  
    var g = svg.selectAll(".data")
        .data(data)
      .enter().append("g")
        .attr("class", "data")
        .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; });
  
    g.append("rect")
        .attr("x", -10)
        .attr("y", -10)
        .attr("width", 20)
        .attr("height", 20);
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    d3.select("#chart-5 button").on("click", function() {
      g
          .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; })
        .select("rect")
          .style("opacity", 1);
  
      g.transition()
          .duration(750)
          .attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")"; })
        .select("rect")
          .style("opacity", 1e-6);
    });
  })();
  </script>
  <p>All data in D3 is specified as an array of values. Conveniently, this mirrors the concept of a selection, which is just an array of elements. Notice then how the first number (the first <em>datum</em>, 32) is bound to the first circle (the first <em>element</em>, on top), the second number is bound to the second circle, and so on.</p>
  
  <p>Once data is bound, that data is accessible as an argument to our attribute and style functions. This means we visually encode data, or in other words, create a visualization! For example, here we set the <em>x</em>-position and radius using the data:</p>
  <div class="chart" id="chart-6">
  <pre class="code">circle.attr("cx", function(d) {
    return d;
  });
  
  circle.attr("r", function(d) {
    return Math.sqrt(d);
  });
  </pre>
  <button>run</button>
  <svg width="360" height="180"><g transform="translate(180,45)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">32</text></g><g transform="translate(60,90)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">57</text></g><g transform="translate(300,135)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">112</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-6").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var g = svg.selectAll("g")
        .data(data)
      .enter().append("g")
        .attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")"; });
  
    g.append("circle")
        .attr("class", "little")
        .attr("r", 12);
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    d3.select("#chart-6 button").on("click", function() {
      g
          .attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")"; })
        .select("circle")
          .attr("r", 12);
  
      g.transition()
          .duration(750)
          .attr("transform", function(d) { return "translate(" + d + "," + y(d) + ")"; })
        .select("circle")
          .attr("r", Math.sqrt);
    });
  })();
  </script>
  <p>There’s a second argument to each function you can use: it specifies the <em>index</em> of the element within its selection. This is a <a href="http://en.wikipedia.org/wiki/Zero-based_numbering">zero-based</a> index, and it’s useful for computing offsets or as a simple way of identifying individual elements. The argument is optional; if you don’t specify it when declaring your function, it will be ignored. For example:</p>
  <div class="chart" id="chart-6b">
  <pre class="code">circle.attr("cx", function(d, i) {
    return i * 100 + 30;
  });
  </pre>
  <button>run</button>
  <svg width="360" height="180"><g transform="translate(180,45)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">0</text></g><g transform="translate(60,90)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">1</text></g><g transform="translate(300,135)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">2</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-6b").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var g = svg.selectAll("g")
        .data(data)
      .enter().append("g")
        .attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")"; });
  
    g.append("circle")
        .attr("class", "little")
        .attr("r", 12);
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(function(d, i) { return i; });
  
    d3.select("#chart-6b button").on("click", function() {
      g
          .attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")"; })
        .transition()
          .duration(750)
          .attr("transform", function(d, i) { return "translate(" + (i * 100 + 30) + "," + y(d) + ")"; });
    });
  })();
  </script>
  <p>Here we use the index <em>i</em> to position the elements sequentially only the <em>x</em>-dimension. Each element is separated by 100 pixels, with an offset of 30 pixels from the left side. In SVG, the origin is in the top-left corner.</p>
  
  <h2 id="creating_elements">creating elements</h2>
  
  <p>But what if we had <em>four</em> numbers to display, rather than three? We wouldn’t have enough circles to display all the numbers. When joining data to elements, D3 stores the leftover data in the <a href="https://github.com/mbostock/d3/wiki/Selections#enter">enter</a> selection. (The terms “enter” and “exit” are adopted from stage terminology.) Here, the fourth number 293 remains in the enter selection, because we only have three circle elements:</p>
  <div class="chart" id="chart-7">
  <pre class="code">var circle = svg.selectAll("circle")
      .data([32, 57, 112, 293]);
  </pre>
  <button>run</button>
  <svg width="360" height="180"><circle class="little" cx="180" cy="45" r="12"></circle><circle class="little" cx="60" cy="90" r="12"></circle><circle class="little" cx="300" cy="135" r="12"></circle><g class="data" transform="translate(20,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">32</text></g><g class="data" transform="translate(40,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">57</text></g><g class="data" transform="translate(60,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">112</text></g><g class="data" transform="translate(80,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">293</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-7").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    svg.selectAll(".little")
        .data(data)
      .enter().append("circle")
        .attr("class", "little")
        .attr("cx", x)
        .attr("cy", y)
        .attr("r", 12);
  
    var g = svg.selectAll(".data")
        .data(dataEnter)
      .enter().append("g")
        .attr("class", "data")
        .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; });
  
    g.append("rect")
        .attr("x", -10)
        .attr("y", -10)
        .attr("width", 20)
        .attr("height", 20);
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    d3.select("#chart-7 button").on("click", function() {
      g
          .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; })
        .select("rect")
          .style("opacity", 1);
  
      g.filter(function(d, i) { return i != 3; }).transition()
          .duration(750)
          .attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")"; })
        .select("rect")
          .style("opacity", 1e-6);
  
      g.select("rect").filter(function(d, i) { return i == 3; })
          .style("fill", "#eee")
          .style("stroke", "#ccc")
        .transition()
          .duration(750)
          .style("fill", "lightgreen")
          .style("stroke", "green");
    });
  })();
  </script>
  <p>Using the enter selection, we can create new circles for any missing data. Each new circle is already bound to the data, so we can use data to compute attributes and styles:</p>
  <div class="chart" id="chart-8">
  <pre class="code">var enter = circle.enter().append("circle");
  
  enter.attr("cy", 90);
  
  enter.attr("cx", 160);
  
  enter.attr("r", function(d) {
    return Math.sqrt(d);
  });
  </pre>
  <button>run</button>
  <svg width="360" height="180"><g transform="translate(180,45)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">32</text></g><g transform="translate(60,90)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">57</text></g><g transform="translate(300,135)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">112</text></g><g class="data" transform="translate(20,20)"></g><g class="data" transform="translate(40,20)"></g><g class="data" transform="translate(60,20)"></g><g class="data" transform="translate(80,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">293</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-8").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var g = svg.selectAll("g")
        .data(data)
      .enter().append("g")
        .attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")"; });
  
    g.append("circle")
        .attr("class", "little")
        .attr("r", 12);
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    var g = svg.selectAll(".data")
        .data(dataEnter)
      .enter().append("g")
        .attr("class", "data")
        .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; })
        .filter(function(d, i) { return i == 3; });
  
    g.append("circle")
        .attr("class", "little")
        .attr("r", 1e-6);
  
    g.append("rect")
        .attr("x", -10)
        .attr("y", -10)
        .attr("width", 20)
        .attr("height", 20)
        .style("fill", "lightgreen")
        .style("stroke", "green");
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    d3.select("#chart-8 button").on("click", function() {
      g.attr("transform", "translate(80,20)");
      g.select("circle").attr("r", 1e-6);
      g.select("rect").style("opacity", 1);
      var t = g.transition().duration(750);
      t.attr("transform", "translate(160,100)");
      t.select("circle").attr("r", Math.sqrt);
      t.select("rect").style("opacity", 1e-6);
    });
  })();
  </script>
  <p>Taking this to the next logical step, then, what if we have <em>no</em> existing elements? Meaning, what if the document is empty? Say we start with an empty page, and we want to create new circles that correspond to our data? Then we’re joining data to an empty selection, and all of the data ends up in <em>enter</em>:</p>
  <div class="chart" id="chart-9">
  <pre class="code">var enter = circle.enter().append("circle");
  
  enter.attr("cy", 90);
  
  enter.attr("cx", function(d) {
    return d;
  });
  
  enter.attr("r", function(d) {
    return Math.sqrt(d);
  });
  </pre>
  <button>run</button>
  <svg width="360" height="180"><g class="data" transform="translate(20,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">32</text></g><g class="data" transform="translate(40,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">57</text></g><g class="data" transform="translate(60,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">112</text></g><g class="data" transform="translate(80,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">293</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-9").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var g = svg.selectAll(".data")
        .data(dataEnter)
      .enter().append("g")
        .attr("class", "data")
        .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; });
  
    g.append("circle")
        .attr("class", "little")
        .attr("r", 1e-6);
  
    g.append("rect")
        .attr("x", -10)
        .attr("y", -10)
        .attr("width", 20)
        .attr("height", 20)
        .style("fill", "lightgreen")
        .style("stroke", "green");
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    d3.select("#chart-9 button").on("click", function() {
      g.attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; });
      g.select("rect").style("opacity", 1);
      g.select("circle").attr("r", 1e-6);
  
      var t = g.transition().duration(750);
      t.attr("transform", function(d, i) { return "translate(" + d + ",90)"; });
      t.select("circle").attr("r", Math.sqrt);
      t.select("rect").style("opacity", 1e-6);
    });
  })();
  </script>
  <p>This pattern is so common, you’ll often see the <a href="https://github.com/mbostock/d3/wiki/Selections#selectAll">selectAll</a> + <a href="https://github.com/mbostock/d3/wiki/Selections#data">data</a> + <a href="https://github.com/mbostock/d3/wiki/Selections#enter">enter</a> + <a href="https://github.com/mbostock/d3/wiki/Selections#append">append</a> operators called sequentially, one immediately after the other. Despite it being common, keep in mind that this is just one special case of a data join; we’ve already seen another common case (selecting elements for update) and we’ll see other interesting cases to consider in a bit.</p>
  
  <p>Another technique you can use to make your code more concise is <a href="http://en.wikipedia.org/wiki/Method_chaining">method chaining</a>. Each operator in D3 returns the current selection, so you can apply multiple operators sequentially. For example, the above code can be rewritten:</p>
  <div class="chart" id="chart-10">
  <pre class="code">svg.selectAll("circle")
      .data([32, 57, 112, 293])
    .enter().append("circle")
      .attr("cy", 90)
      .attr("cx", String)
      .attr("r", Math.sqrt);
  </pre>
  <button>run</button>
  <svg width="360" height="180"><g class="data" transform="translate(20,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">32</text></g><g class="data" transform="translate(40,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">57</text></g><g class="data" transform="translate(60,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">112</text></g><g class="data" transform="translate(80,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">293</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-10").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var g = svg.selectAll(".data")
        .data(dataEnter)
      .enter().append("g")
        .attr("class", "data")
        .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; });
  
    g.append("circle")
        .attr("class", "little")
        .attr("r", 1e-6);
  
    g.append("rect")
        .attr("x", -10)
        .attr("y", -10)
        .attr("width", 20)
        .attr("height", 20)
        .style("fill", "lightgreen")
        .style("stroke", "green");
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    d3.select("#chart-10 button").on("click", function() {
      g.attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; });
      g.select("rect").style("opacity", 1);
      g.select("circle").attr("r", 1e-6);
  
      var t = g.transition().duration(750);
      t.attr("transform", function(d, i) { return "translate(" + d + ",90)"; });
      t.select("circle").attr("r", Math.sqrt);
      t.select("rect").style("opacity", 1e-6);
    });
  })();
  </script>
  <p>As you can see, the code is made even smaller using built-in JavaScript functions, rather than defining anonymous ones. The built-in <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String">String</a> method, for example, is a shorthand way of using JavaScript’s default string conversion to compute the attribute value from the associated data. Similarly, we can plug in <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Math/sqrt">Math.sqrt</a> to set the radius attribute as the square root of the associated data. This technique of plugging in reusable functions to compute attribute values is used extensively in D3, particularly in conjunction with <a href="https://github.com/mbostock/d3/wiki/Quantitative-Scales">scales</a> and <a href="https://github.com/mbostock/d3/wiki/SVG-Shapes">shapes</a>.</p>
  
  <h2 id="destroying_elements">destroying elements</h2>
  
  <p>Sometimes you have the opposite problem from creation: you have too <em>many</em> existing elements, and you want to remove them. You can select nodes and <a href="https://github.com/mbostock/d3/wiki/Selections#remove">remove</a> them, but more commonly, you’ll use the <a href="https://github.com/mbostock/d3/wiki/Selections#exit">exit</a> selection to let D3 determine which elements are exiting the stage. The exit selection is the opposite of the enter selection: it contains all elements for which there is no corresponding data.</p>
  <div class="chart" id="chart-11">
  <pre class="code">var circle = svg.selectAll("circle")
      .data([32, 57]);
  </pre>
  <button>run</button>
  <svg width="360" height="180"><circle class="little" cx="180" cy="45" r="12"></circle><circle class="little" cx="60" cy="90" r="12"></circle><circle class="little" cx="300" cy="135" r="12"></circle><g class="data" transform="translate(20,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">32</text></g><g class="data" transform="translate(40,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">57</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-11").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var circle = svg.selectAll(".little")
        .data(data)
      .enter().append("circle")
        .attr("class", "little")
        .attr("cx", x)
        .attr("cy", y)
        .attr("r", 12)
        .filter(function(d, i) { return i == 2; });
  
    var g = svg.selectAll(".data")
        .data(dataExit)
      .enter().append("g")
        .attr("class", "data")
        .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; });
  
    g.append("rect")
        .attr("x", -10)
        .attr("y", -10)
        .attr("width", 20)
        .attr("height", 20);
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    d3.select("#chart-11 button").on("click", function() {
      g
          .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; })
        .select("rect")
          .style("opacity", 1);
  
      g.transition()
          .duration(750)
          .attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")"; })
        .select("rect")
          .style("opacity", 1e-6);
  
      circle
          .style("fill", "#aaa")
          .style("stroke", "#666")
        .transition()
          .duration(750)
          .style("fill", "lightcoral")
          .style("stroke", "red");
    });
  })();
  </script>
  <p>All that’s left to do, then, is to remove the exiting elements:</p>
  <div class="chart" id="chart-12">
  <pre class="code">circle.exit().remove();
  </pre>
  <button>run</button>
  <svg width="360" height="180"><g class="data" transform="translate(180,45)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">32</text></g><g class="data" transform="translate(60,90)"><circle class="little" r="12"></circle><text dy=".35em" text-anchor="middle">57</text></g><g class="data" transform="translate(300,135)"><circle class="little" r="12" style="fill: #f08080; stroke: #ff0000;"></circle><text dy=".35em" text-anchor="middle">112</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-12").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var g = svg.selectAll(".data")
        .data(data)
      .enter().append("g")
        .attr("class", "data")
        .attr("transform", function(d) { return "translate(" + x(d) + "," + y(d) + ")"; });
  
    g.append("circle")
        .attr("class", "little")
        .attr("r", 12);
  
    g.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    g = g.filter(function(d, i) { return i == 2; });
  
    g.select("circle")
        .style("fill", "lightcoral")
        .style("stroke", "red");
  
    d3.select("#chart-12 button").on("click", function() {
      g.select("circle").attr("r", 12);
      g.select("text").style("opacity", 1);
      var t = g.transition().duration(750);
      t.select("circle").attr("r", 1e-6);
      t.select("text").style("opacity", 1e-6);
    });
  })();
  </script>
  <p>The enter, update and exit selections are computed by the <a href="https://github.com/mbostock/d3/wiki/Selections#data">data</a> operator, and don’t change when you append or remove elements—at least until you call <a href="https://github.com/mbostock/d3/wiki/Selections#selectAll">selectAll</a> again. So, if you keep variables around that point to selections (such as <code>circle</code>, above), you’ll probably want to reselect after adding or removing elements.</p>
  
  <h2 id="all_together_now">all together now</h2>
  
  <p>Putting everything together, consider the three possible outcomes that result from joining data to elements:</p>
  
  <ol>
  <li>enter - incoming actors, entering the stage.</li>
  
  <li>update - persistent actors, staying on stage.</li>
  
  <li>exit - outgoing actors, exiting the stage.</li>
  </ol>
  
  <p>When we use the default join-by-index, either the enter or exit selection will be empty (or both): if there are more data than elements, the extra data are in the enter selection; if there are fewer data than elements, the extra elements are in the exit selection. However, by specifying a key function to the data operator, we can control exactly how data is bound to elements. And in this case, we have both enter and exit.</p>
  <div class="chart" id="chart-13">
  <pre class="code">var circle = svg.selectAll("circle")
      .data([32, 57, 293], String);
  
  circle.enter().append("circle")
      .attr("cy", 90)
      .attr("cx", String)
      .attr("r", Math.sqrt);
  
  circle.exit().remove();
  </pre>
  <button>run</button>
  <svg width="360" height="180"><g class="data" transform="translate(20,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">32</text></g><g class="data" transform="translate(40,20)"><rect x="-10" y="-10" width="20" height="20"></rect><text dy=".35em" text-anchor="middle">57</text></g><g class="data" transform="translate(60,20)"><circle class="little" r="0.000001"></circle><rect x="-10" y="-10" width="20" height="20" style="fill: #90ee90; stroke: #008000;"></rect><text dy=".35em" text-anchor="middle">293</text></g><g class="element" transform="translate(32,90)"><circle class="little" r="5.656854249492381"></circle><text dy=".35em" text-anchor="middle">32</text></g><g class="element" transform="translate(57,90)"><circle class="little" r="7.54983443527075"></circle><text dy=".35em" text-anchor="middle">57</text></g><g class="element" transform="translate(112,90)"><circle class="little" r="10.583005244258363" style="fill: #f08080; stroke: #ff0000;"></circle><text dy=".35em" text-anchor="middle">112</text></g></svg></div><script type="text/javascript">
  (function() {
    var svg = d3.select("#chart-13").append("svg")
        .attr("width", w)
        .attr("height", h);
  
    var gd = svg.selectAll(".data")
        .data([32, 57, 293])
      .enter().append("g")
        .attr("class", "data")
        .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; });
  
    var ed = gd.filter(function(d, i) { return i == 2; }),
        ud = gd.filter(function(d, i) { return i != 2; });
  
    ed.append("circle")
        .attr("class", "little")
        .attr("r", 1e-6);
  
    gd.append("rect")
        .attr("x", -10)
        .attr("y", -10)
        .attr("width", 20)
        .attr("height", 20);
  
    gd.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    var ge = svg.selectAll(".element")
        .data(data)
      .enter().append("g")
        .attr("class", "element")
        .attr("transform", function(d) { return "translate(" + d + ",90)"; });
  
    ge.append("circle")
        .attr("class", "little")
        .attr("r", Math.sqrt);
  
    ge.append("text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text(String);
  
    ed.select("rect")
        .style("fill", "lightgreen")
        .style("stroke", "green");
  
    var xe = ge.filter(function(d, i) { return i == 2; });
  
    xe.select("circle")
        .style("fill", "lightcoral")
        .style("stroke", "red");
  
    d3.select("#chart-13 button").on("click", function() {
      gd
          .attr("transform", function(d, i) { return "translate(" + 20 * (i + 1) + ",20)"; })
        .transition()
          .duration(750)
          .attr("transform", function(d) { return "translate(" + d + ",90)"; });
  
      gd.select("rect")
          .style("opacity", 1)
        .transition()
          .duration(750)
          .style("opacity", 1e-6);
  
      ed.select("circle")
          .attr("r", 1e-6)
        .transition()
          .duration(750)
          .attr("r", Math.sqrt);
  
      xe.select("circle")
          .attr("r", Math.sqrt)
        .transition()
          .duration(750)
          .attr("r", 1e-6);
  
      xe.select("text")
          .style("opacity", 1)
        .transition()
          .duration(750)
          .style("opacity", 1e-6);
    });
  })();
  </script>
  <p>Want to learn more about selections and transitions? Read <a href="http://mbostock.github.io/d3/tutorial/bar-2.html">A Bar Chart, Part 2</a> for a practical example of using enter and exit to display realtime data.</p>
  
        </div>
        <div class="foot">Copyright © 2012 <a href="http://bost.ocks.org/mike/">Mike Bostock</a></div>
      </div>
    
    <a href="https://github.com/mbostock/d3"><img style="position:absolute;top:0;right:0;border:0;" width="149" height="149" src="./circle_files/forkme.png" alt="Fork me on GitHub"></a>
    
  
  </body></html>
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.